////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2002.
// -------------------------------------------------------------------------
//  File name:   3dengine.cpp
//  Version:     v1.00
//  Created:     28/5/2001 by Vladimir Kajalin
//  Compilers:   Visual Studio.NET
//  Description: Implementation of I3DEngine interface methods
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <ICryAnimation.h>
#include <IFaceGen.h>
#include <IGameFramework.h>
#include "IAudioDevice.h"

#include "3dEngine.h"
#include "terrain.h"
#include "VisAreas.h"
#include "ObjMan.h"
#include "terrain_water.h"

#include "DecalManager.h"
#include "Vegetation.h"
#include "IndexedMesh.h"
#include "WaterVolumes.h"

#include "MatMan.h"

#include "Brush.h"
#include "CullBuffer.h"
#include "CGF/CGFLoader.h"
#include "CGF/ReadOnlyChunkFile.h"

#include "CloudRenderNode.h"
#include "CloudsManager.h"
#include "SkyLightManager.h"
#include "FogVolumeRenderNode.h"
#include "RoadRenderNode.h"
#include "DecalRenderNode.h"
#include "TimeOfDay.h"
#include "VoxMan.h"
#include "LightEntity.h"
#include "FogVolumeRenderNode.h"
#include "ObjectsTree.h"
#include "WaterVolumeRenderNode.h"
#include "WaterWaveRenderNode.h"
#include "DistanceCloudRenderNode.h"
#include "VolumeObjectRenderNode.h"
#include "AutoCubeMapRenderNode.h"
#include "WaterWaveRenderNode.h"
#include "RopeRenderNode.h"
#include "RenderMeshMerger.h"
#include "PhysCallbacks.h"
#include "VoxTerrain.h"
#include "LPVRenderNode.h"
#include "GlobalIllumination.h"
#include "DeferredCollisionEvent.h"
#include "LightShapeRenderNode.h"
#include "BreakableGlassRenderNode.h"

#if !defined(EXCLUDE_DOCUMENTATION_PURPOSE)
#include "PrismRenderNode.h"
#endif // EXCLUDE_DOCUMENTATION_PURPOSE

// requiered for LARGE_INTEGER used by QueryPerformanceCounter
#ifdef WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif

uint Cry3DEngineBase::m_nMainThreadId = 0;
bool Cry3DEngineBase::m_bRenderTypeEnabled[eERType_TypesNum];
ISystem * Cry3DEngineBase::m_pSystem=0;
IRenderer * Cry3DEngineBase::m_pRenderer=0;
ITimer * Cry3DEngineBase::m_pTimer=0;
ILog * Cry3DEngineBase::m_pLog=0;
IPhysicalWorld * Cry3DEngineBase::m_pPhysicalWorld=0;
CTerrain * Cry3DEngineBase::m_pTerrain=0;
#ifdef USE_VOXEL_TERRAIN
class CVoxTerrain * Cry3DEngineBase::m_pVoxTerrain=0;
#else
const class CVoxTerrain * Cry3DEngineBase::m_pVoxTerrain=0;
#endif
CObjManager * Cry3DEngineBase::m_pObjManager=0;
IConsole * Cry3DEngineBase::m_pConsole=0;
C3DEngine * Cry3DEngineBase::m_p3DEngine=0;
CVars * Cry3DEngineBase::m_pCVars=0;
ICryPak * Cry3DEngineBase::m_pCryPak=0;
IParticleManager * Cry3DEngineBase::m_pPartManager=0;
CDecalManager  * Cry3DEngineBase::m_pDecalManager=0;
CSkyLightManager* Cry3DEngineBase::m_pSkyLightManager=0;
CCloudsManager * Cry3DEngineBase::m_pCloudsManager=0;
CVisAreaManager* Cry3DEngineBase::m_pVisAreaManager=0;
CWaterWaveManager  * Cry3DEngineBase::m_pWaterWaveManager=0;
CRenderMeshMerger  * Cry3DEngineBase::m_pRenderMeshMerger = 0;
CMatMan        * Cry3DEngineBase::m_pMatMan=0;
//IStreamedObjectListener* Cry3DEngineBase::m_pStreamListener = 0;

int Cry3DEngineBase::m_nRenderStackLevel=-1;
float Cry3DEngineBase::m_fZoomFactor=1.f;
float Cry3DEngineBase::m_fInvZoomFactor=1.f;
float Cry3DEngineBase::m_fRealZoomFactor=1.f;
float Cry3DEngineBase::m_fInvDissolveDistBand=1.f;
int Cry3DEngineBase::m_nZoomMode=0;
bool Cry3DEngineBase::m_bZoomInProgress=false;
int Cry3DEngineBase::m_dwRecursionDrawFlags[MAX_RECURSION_LEVELS];
int Cry3DEngineBase::m_nRenderFrameID = 0;
uint32 Cry3DEngineBase::m_nRenderMainFrameID = 0;
int Cry3DEngineBase::m_nRenderThreadListID = 0;
bool Cry3DEngineBase::m_bProfilerEnabled = false;
int Cry3DEngineBase::m_CpuFlags=0;
#if (!defined(PS3) && !defined(XENON) && !defined(gringo))
bool Cry3DEngineBase::m_bEditor = false;
#endif
ESystemConfigSpec Cry3DEngineBase::m_LightConfigSpec = CONFIG_VERYHIGH_SPEC;
IMaterialManager* Cry3DEngineBase::m_pMaterialManager = NULL;
CCamera Cry3DEngineBase::m_Camera;
int Cry3DEngineBase::m_arrInstancesCounter[eERType_TypesNum];
IGetLayerIdAtCallback * C3DEngine::m_pGetLayerIdAtCallback = 0;

#define LAST_POTENTIALLY_VISIBLE_TIME 2

// The only direct particle function needed by 3DEngine, implemented in the same DLL.
extern IParticleManager* CreateParticleManager(bool bEnable);

//////////////////////////////////////////////////////////////////////
C3DEngine::C3DEngine(ISystem	* pSystem)
{
  //#if defined(_DEBUG) && defined(WIN32)
  //	_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
  //#endif

  // Level info
  m_fSunSpecMult = 1.f;
  m_bAreaActivationInUse = false;

  CVegetation::InitVegDecomprTable();

  memset(m_dwRecursionDrawFlags,0,sizeof(m_dwRecursionDrawFlags));

  Cry3DEngineBase::m_nMainThreadId = CryGetCurrentThreadId();
  Cry3DEngineBase::m_pSystem=pSystem;
  Cry3DEngineBase::m_pRenderer=gEnv->pRenderer;
  Cry3DEngineBase::m_pTimer=gEnv->pTimer;
  Cry3DEngineBase::m_pLog=gEnv->pLog;
  Cry3DEngineBase::m_pPhysicalWorld=gEnv->pPhysicalWorld;
  Cry3DEngineBase::m_pConsole=gEnv->pConsole;
  Cry3DEngineBase::m_p3DEngine=this;
  Cry3DEngineBase::m_pCryPak=gEnv->pCryPak;
  Cry3DEngineBase::m_pCVars=0;
	Cry3DEngineBase::m_pRenderMeshMerger = new CRenderMeshMerger;
	//Cry3DEngineBase::m_pStreamListener = NULL;
  Cry3DEngineBase::m_CpuFlags=pSystem->GetCPUFlags();
  memset(Cry3DEngineBase::m_arrInstancesCounter,0,sizeof(Cry3DEngineBase::m_arrInstancesCounter));

#if (!defined(PS3) && !defined(XENON) && !defined(gringo))
  m_bEditor = gEnv->IsEditor();
#endif

  m_pCVars            = new CVars();
  Cry3DEngineBase::m_pCVars = m_pCVars;

#ifdef _DEBUG
#ifndef _XBOX
  //  _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); // _crtBreakAlloc
#endif
#endif

  m_pMatMan = new CMatMan();
	m_pMatMan->InitDefaults();
  
  m_pTimeOfDay = NULL;

  m_szLevelFolder[0]=0;

  m_pSun=0;
  m_nFlags=0;
  m_pSkyMat = 0;
  m_pSkyLowSpecMat = 0;
  m_pTerrainWaterMat = 0;
  m_pVoxTerrainMat = 0;
  m_nWaterBottomTexId=0;
  m_vSunDir = Vec3(5.f, 5.f, DISTANCE_TO_THE_SUN);
  m_vSunDirRealtime = Vec3(5.f, 5.f, DISTANCE_TO_THE_SUN).GetNormalized();

  m_pTerrain=0;	

  m_nBlackTexID = 0;

  // create components
  m_pObjManager = new CObjManager();

  m_pDecalManager     = 0;//new CDecalManager   (m_pSystem, this);
  m_pCloudsManager    = new CCloudsManager;
  m_pPartManager			= 0;
  m_pVisAreaManager   = 0;
  m_pSkyLightManager  = new CSkyLightManager();
  m_pWaterWaveManager = 0;

  // create REs
  m_pRESky              = 0;
  m_pREHDRSky           = 0;

  m_pFarTreeSprites   = 0;

  m_pPhysMaterialEnumerator=0;

  m_fMaxViewDistHighSpec = 8000;
  m_fMaxViewDistLowSpec  = 1000;
  m_fTerrainDetailMaterialsViewDistRatio = 1.f;

  m_fSkyBoxAngle=0;
  m_fSkyBoxStretching=0;

  m_pGlobalWind	= 0;
  m_vWindSpeed(1,0,0);

  m_bOcean = true;
  m_bCameraUnderWater = false;
  m_nOceanRenderFlags = 0;

  m_bSunShadows = m_bShowTerrainSurface = true;
  m_bGsmExtendLastLod = GetCVars()->e_GsmExtendLastLod == 1;
  m_nGsmLodsNum = GetCVars()->e_GsmLodsNum;

  m_nRenderLightsNum=m_nRealLightsNum=m_nDeferredLightsNum=0;
	m_bHasDirtyDynLights = false;

  m_pCoverageBuffer = new CCullBuffer();

  union {
    CStatObjFoliage* CStatObjFoliage::*next;
    INT_PTR inext;
  } tmp;




  tmp.inext = 0; tmp.next = &CStatObjFoliage::m_next;

  m_pFirstFoliage = m_pLastFoliage = (CStatObjFoliage*)((INT_PTR)&m_pFirstFoliage-tmp.inext);

	m_fHDRDynamicMultiplier=2.0f;
	
  m_fHDRShoulderScale=1.0f;
  m_fHDRMidtonesScale=1.0f;
  m_fHDRToeScale=1.0f;
  m_fHDRWhitePoint=4.0f;  
  m_vColorBalance = Vec3(1.0f, 1.0f, 1.0f);
  m_fHDRContrast=1.0f;
  m_fHDRSaturation=1.0f;


  m_vSkyHightlightPos.Set(0,0,0);
  m_vSkyHightlightCol.Set(0,0,0);
  m_fSkyHighlightSize = 0;

	m_eShadowMapRendering = SHADOW_MAP_NONE;

	m_volFogGlobalDensity = 0.02f;
	m_volFogGlobalDensityMultiplierLDR = 1.0f;
	m_volFogFinalDensityClamp = 1.0f;

  //	m_pObjectsTree[nSID] = NULL;
  //  m_pSceneTree = NULL;

  m_idMatLeaves = -1;

  /* // structures should not grow too much - commented out as in 64bit they do
  assert(sizeof(CVegetation)-sizeof(IRenderNode)<=52);
  assert(sizeof(CBrush)-sizeof(IRenderNode)<=120);
  assert(sizeof(IRenderNode)<=96);
  */
  m_oceanFogColor = 0.2f * Vec3( 29.0f, 102.0f, 141.0f ) / 255.0f;
  m_oceanFogColorShallow = Vec3(0,0,0); //0.5f * Vec3( 206.0f, 249.0f, 253.0f ) / 255.0f;
  m_oceanFogDensity = 0; //0.2f;

  m_oceanCausticsDistanceAtten = 100.0f; 
  m_oceanCausticsMultiplier = 1.0f;  
  m_oceanCausticsDarkeningMultiplier = 1.0f;

  m_oceanCausticHeight = 2.5f;
  m_oceanCausticDepth = 8.0f;
  m_oceanCausticIntensity = 1.0f;

  m_oceanWindDirection = 1;
  m_oceanWindSpeed = 4.0f;
  m_oceanWavesSpeed = 5.0f;
  m_oceanWavesAmount = 1.5f;
  m_oceanWavesSize = 0.75f;

	m_hdrBlueShift = Vec3( 204.0f, 204.0f, 255.0f) / 255.0f;
	m_hdrBloomColor = Vec3( 255.0f,255.0f,255.0f) / 255.0f;
	m_hdrBloomMul = 1.0f;
	m_hdrFlaresColor = Vec3( 255.0f,255.0f,255.0f) / 255.0f;
	m_hdrFlaresMul = 0.1f;
	m_hdrStreaksColor = Vec3( 255.0f,255.0f,255.0f) / 255.0f;
	m_hdrStreaksMul = 0.33f;
	m_hdrStreakScale = 1.0f;

  m_fRefreshSceneDataCVarsSumm = -1;
  m_nRenderTypeEnableCVarSum = -1;

  if (!m_LTPRootFree.pNext)
  {
    m_LTPRootFree.pNext = &m_LTPRootFree;
    m_LTPRootFree.pPrev = &m_LTPRootFree;
  }

  if (!m_LTPRootUsed.pNext)
  {
    m_LTPRootUsed.pNext = &m_LTPRootUsed;
    m_LTPRootUsed.pPrev = &m_LTPRootUsed;
  }
  /*
  CTerrainNode::SetProcObjChunkPool(new SProcObjChunkPool);
  CTerrainNode::SetProcObjPoolMan(new CProcVegetPoolMan);
  */
  m_bResetRNTmpDataPool = false;

  m_fSunDirUpdateTime = 0;
  m_vSunDirNormalized.zero();

	m_volFogRamp = Vec3(0, 100.0f, 0);
	m_volFogShadowRange = Vec3(0.1f, 0, 0);
	m_volFogShadowDarkening = Vec3(0.25f, 1, 1);
	m_volFogShadowEnable = Vec3(0, 0, 0);
	m_nightSkyHorizonCol = Vec3(0,0,0);
	m_nightSkyZenithCol = Vec3(0,0,0);
	m_nightSkyZenithColShift = 0;
	m_nightSkyStarIntensity = 0;
	m_moonDirection = Vec3(0,0,0);
	m_nightMoonCol = Vec3(0,0,0);
	m_nightMoonSize = 0;
	m_nightMoonInnerCoronaCol = Vec3(0,0,0);
	m_nightMoonInnerCoronaScale = 1.0f;
	m_nightMoonOuterCoronaCol = Vec3(0,0,0);
	m_nightMoonOuterCoronaScale = 1.0f;
	m_sunRotationZ = 0;
	m_sunRotationLongitude = 90.0f;
	m_moonRotationLatitude = 0;
	m_moonRotationLongitude = 0;
	m_oceanFogColorMultiplier = 0;
	m_skyboxMultiplier = 1.0f;
	m_dayNightIndicator = 1.0f;
	m_fogColor2 = Vec3(0,0,0);
	m_fogColorRadial = Vec3(0,0,0);
	m_volFogHeightDensity = Vec3(0,1,0);
	m_volFogHeightDensity2 = Vec3(4000.0f,0,0);
	m_volFogGradientCtrl = Vec3(1,1,1);

	m_vFogColor = Vec3(1.0f,1.0f,1.0f);
	m_vAmbGroundCol = Vec3(0.0f,0.0f,0.0f);

  m_dawnStart = 350.0f / 60.0f;
  m_dawnEnd = 360.0f / 60.0f;
  m_duskStart = 12.0f + 360.0f / 60.0f;
  m_duskEnd = 12.0f + 370.0f / 60.0f;

	m_fCloudShadingSunLightMultiplier = 0;
	m_fCloudShadingSkyLightMultiplier = 0;
	m_vCloudShadingCustomSunColor = Vec3(0,0,0);
	m_vCloudShadingCustomSkyColor = Vec3(0,0,0);

  m_vPrevMainFrameCamPos.Set(-1000000.f,-1000000.f,-1000000.f);
  m_fAverageCameraSpeed = 0;
  m_vAverageCameraMoveDir = Vec3(0);
  m_bContentPrecacheRequested = false;
  m_bTerrainTextureStreamingInProgress = false;
  m_bLayersActivated = false;
	m_eShadowMode = ESM_NORMAL;
  m_pSegmentsManager = 0;
  m_bSegmentedWorldActive = false;

  //  for(int i=0 ;i<MAX_SURFACE_TYPES_COUNT; i++)
  //  m_arrSurfTypeMapping[i] = -1;

  m_pGlobalIlluminationManager = NULL;

  ClearDebugFPSInfo();

  m_fMaxViewDistScale=1.f;

  m_ptexIconLowMemoryUsage=NULL;
  m_ptexIconAverageMemoryUsage=NULL;
  m_ptexIconHighMemoryUsage=NULL;
  m_ptexIconEditorConnectedToConsole=NULL;
  m_pScreenshotCallback = 0;
  m_bInShutDown = false;
  m_bInUnload = false;
  m_bInLoad = false;

	m_nCloudShadowTexId = 0;
	m_vCloudShadowAnimParams = Vec3(ZERO);
	m_vCloudShadowTileParams = Vec4(ZERO, 0);

	m_nNightMoonTexId = 0;
	m_bendingPoolEntryCount = 0u; 
	
  m_pDeferredPhysicsEventManager = new CDeferredPhysicsEventManager();
}

//////////////////////////////////////////////////////////////////////
C3DEngine::~C3DEngine()
{
  m_bInShutDown = true;
  m_bInUnload = true;
  m_bInLoad = false;

  delete CTerrainNode::GetProcObjPoolMan();
  CTerrainNode::SetProcObjChunkPool(NULL);

  delete CTerrainNode::GetProcObjChunkPool();
  CTerrainNode::SetProcObjPoolMan(NULL);

  assert(IsHeapValid());

  ShutDown();

  delete m_pTimeOfDay;
  delete m_pDecalManager; 
  delete m_pVisAreaManager;

  delete m_pCoverageBuffer; m_pCoverageBuffer=0;
  delete m_pSkyLightManager;
  for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); nSID++)
    SAFE_DELETE(m_pObjectsTree[nSID]);
  //  delete m_pSceneTree;
  delete m_pRenderMeshMerger;

  delete m_pCloudsManager;

  delete m_pMatMan; m_pMatMan=0;
  delete m_pCVars;

  delete m_pDeferredPhysicsEventManager;

}

//////////////////////////////////////////////////////////////////////

void C3DEngine::RemoveEntInFoliage(int i, IPhysicalEntity *pent)
{
  IPhysicalEntity *pent1 = m_pPhysicalWorld->GetPhysicalEntityById(m_arrEntsInFoliage[i].id);
  pe_params_foreign_data pfd;
  if (!pent)
    pent = pent1;
  else if (pent!=pent1)
  {	// probably someone overwrote foreign flags 
    for(i=m_arrEntsInFoliage.size()-1; i>=0 && pent!=m_pPhysicalWorld->GetPhysicalEntityById(m_arrEntsInFoliage[i].id); i--);
    if (i<0)
      return;
  }
  if (pent && pent->GetParams(&pfd) && (pfd.iForeignFlags>>8&255)==i+1)
  {
    MARK_UNUSED pfd.pForeignData,pfd.iForeignData,pfd.iForeignFlags;
    pfd.iForeignFlagsAND = 255;
    pent->SetParams(&pfd,1);
  }
  int j = m_arrEntsInFoliage.size()-1;
  if (i<j)
  {
    m_arrEntsInFoliage[i] = m_arrEntsInFoliage[j];
    if ((pent=m_pPhysicalWorld->GetPhysicalEntityById(m_arrEntsInFoliage[i].id)) && pent->GetParams(&pfd) && 
      (pfd.iForeignFlags>>8&255)==j+1)
    {
      MARK_UNUSED pfd.pForeignData,pfd.iForeignData;
      pfd.iForeignFlags = pfd.iForeignFlags & 255 | (i+1)<<8;
      pent->SetParams(&pfd,1);
    }
  }
  m_arrEntsInFoliage.DeleteLast();
}

bool C3DEngine::Init()
{
  m_pPartManager = CreateParticleManager(!gEnv->IsDedicated());
  m_pSystem->SetIParticleManager(m_pPartManager);

  for(int i = 0; i < eERType_TypesNum; i++)
  {
    m_bRenderTypeEnabled[i] = true;
  }

  UpdateRenderTypeEnableLookup();

  // Allocate the temporary pool used for allocations during streaming and loading 
  const size_t tempPoolSize = static_cast<size_t>(GetCVars()->e_3dEngineTempPoolSize) << 10; 
  CRY_ASSERT_MESSAGE(tempPoolSize != 0, "C3DEngine::Init() temp pool size is set to 0");

  {
    MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Other, 0, "3Engine Temp Pool");

    if (!CTemporaryPool::Initialize(tempPoolSize))
    {
      CryFatalError("C3DEngine::Init() could not initialize temporary pool");
      return false; 
    }
  }

  return  (true);
}

bool C3DEngine::IsCameraAnd3DEngineInvalid(const CCamera cam, const char * szCaller)
{
	if ( !m_pObjManager )
	{
		return (true);
	}

	const float MAX_M23_REPORTED = 3000000.f; // MAT => upped from 100,000 which spammed this message on spear and cityhall. Really should stop editor generating
	// water levels that trigger this message.

	if( !_finite(cam.GetMatrix().m03) || !_finite(cam.GetMatrix().m13) || !_finite(cam.GetMatrix().m23) || GetMaxViewDistance()<=0 ||
		cam.GetMatrix().m23 < -MAX_M23_REPORTED || cam.GetMatrix().m23 > MAX_M23_REPORTED || cam.GetFov() < 0.0001f || cam.GetFov() > gf_PI)
	{
		Error("Bad camera passed to 3DEngine from %s: Pos=(%.1f, %.1f, %.1f), Fov=%.1f, MaxViewDist=%.1f. Maybe the water level is too extreme.", 
			szCaller, 
			cam.GetMatrix().m03, cam.GetMatrix().m13, cam.GetMatrix().m23, 
			cam.GetFov(), _finite(cam.GetMatrix().m03) ? GetMaxViewDistance() : 0);
		return true;
	}

	return false;

}

void C3DEngine::OnFrameStart()
{
  FUNCTION_PROFILER_3DENGINE;

	if (m_pPartManager)
		m_pPartManager->OnFrameStart();
	m_nRenderWorldUSecs = 0;
	m_pDeferredPhysicsEventManager->Update();
	m_bendingPoolEntryCount = 0u;
}

float g_oceanLevel,g_oceanStep;
float GetOceanLevelCallback(int ix,int iy)
{
  return gEnv->p3DEngine->GetAccurateOceanHeight(Vec3(ix*g_oceanStep,iy*g_oceanStep,g_oceanLevel));	
  /*pe_status_waterman psw;
  gEnv->pPhysicalWorld->GetWatermanStatus(&psw);
  Vec2 pos = Vec2((float)ix,(float)iy)*GetFloatCVar(e_PhysOceanCell)-Vec2(psw.origin);
  Vec2i itile(FtoI(pos.x*0.25f-0.5f), FtoI(pos.y*0.25f-0.5f));
  if ((itile.x|itile.y)<0 || max(itile.x,itile.y)>6)
  return g_oceanLevel;
  SWaterTileBase *pTile = psw.pTiles[itile.x+itile.y*7];
  if (!pTile || !pTile->bActive)
  return g_oceanLevel;
  Vec2i ipos(FtoI((pos.x-itile.x*4)*10.0f-0.5f), FtoI((pos.y-itile.y*4)*10.0f-0.5f));
  return psw.origin.z+pTile->ph[itile.x+itile.y*40];*/
}
unsigned char GetOceanSurfTypeCallback(int ix,int iy)
{
  return 0;	
}

void C3DEngine::Update()
{
  m_bProfilerEnabled = gEnv->pFrameProfileSystem->IsProfiling();

  FUNCTION_PROFILER_3DENGINE;

  m_LightConfigSpec = (ESystemConfigSpec)GetCurrentLightSpec();

  ///	if(m_pVisAreaManager)
  //	m_pVisAreaManager->Preceche(m_pObjManager);

  if (GetObjManager())
    GetObjManager()->ClearStatObjGarbage();

	if(m_pGlobalIlluminationManager)
		m_pGlobalIlluminationManager->UpdatePosition(GetCamera());

  if(m_pTerrain)
  {
    m_pTerrain->Voxel_Recompile_Modified_Incrementaly_Objects();
    m_pTerrain->Recompile_Modified_Incrementaly_RoadRenderNodes();
  }

  if(m_bEditor)
    CRoadRenderNode::FreeStaticMemoryUsage();

  if(m_pDecalManager)
    m_pDecalManager->Update(GetTimer()->GetFrameTime());

  if(GetCVars()->e_PrecacheLevel == 3)
    PrecacheLevel(true,0,0);

  if(GetCVars()->e_VoxelGenerate)
    GetTerrain()->BuildVoxelSpace();

  DebugDraw_Draw();

  ProcessCVarsChange();

  float dt = GetTimer()->GetFrameTime();

  CStatObjFoliage *pFoliage,*pFoliageNext;
  for(pFoliage=m_pFirstFoliage; &pFoliage->m_next!=&m_pFirstFoliage; pFoliage=pFoliageNext) {
    pFoliageNext = pFoliage->m_next;
    pFoliage->Update(dt);
  }

  for(int i=m_arrEntsInFoliage.size()-1;i>=0;i--) if ((m_arrEntsInFoliage[i].timeIdle+=dt)>0.3f)
    RemoveEntInFoliage(i);

  pe_params_area pa;
  IPhysicalEntity *pArea;
  if ((pArea = gEnv->pPhysicalWorld->AddGlobalArea())->GetParams(&pa))
  {
    g_oceanLevel = GetWaterLevel();
    bool bOceanEnabled = GetFloatCVar(e_PhysOceanCell)>0 && g_oceanLevel>0;

    if (bOceanEnabled && (!pa.pGeom || g_oceanStep!=GetFloatCVar(e_PhysOceanCell)))
    {
      new(&pa) pe_params_area;
      primitives::heightfield hf;
      hf.origin.zero().z = g_oceanLevel;
      g_oceanStep = GetFloatCVar(e_PhysOceanCell);
      hf.size.set((int)(8192/g_oceanStep),(int)(8192/g_oceanStep));
      hf.step.set(g_oceanStep,g_oceanStep);
      hf.fpGetHeightCallback = GetOceanLevelCallback;
      hf.fpGetSurfTypeCallback = GetOceanSurfTypeCallback;
      hf.Basis.SetIdentity();
      hf.bOriented = 0;
      hf.typemask = 255;
      hf.typehole = 255;
      hf.heightscale = 1.0f;
      pa.pGeom = gEnv->pPhysicalWorld->GetGeomManager()->CreatePrimitive(primitives::heightfield::type, &hf);
      pArea->SetParams(&pa);

      Vec4 par0,par1;
      GetOceanAnimationParams(par0,par1);
      pe_params_buoyancy pb;
      pb.waterFlow = Vec3(par1.z,par1.y,0)*(par0.z*0.25f); // tone down the speed
      //pArea->SetParams(&pb);
    } 
    else if (!bOceanEnabled && pa.pGeom)
    {
      new(&pa) pe_params_area;
      pa.pGeom = 0;
      pArea->SetParams(&pa);
      pe_params_buoyancy pb;
      pb.waterFlow.zero();
      pArea->SetParams(&pb);
    }
  }

  CRenderMeshUtils::ClearHitCache();

  CleanUpOldDecals();

  CDecalRenderNode::ResetDecalUpdatesCounter();

  m_bTerrainTextureStreamingInProgress = false;
  if(GetTerrain() && GetTerrain()->GetNotReadyTextureNodesCount())
    m_bTerrainTextureStreamingInProgress = true;
	
	DrawVegetationProfiler();
}

void C3DEngine::ProcessCVarsChange()
{
  static int nObjectLayersActivation = -1;

  if(nObjectLayersActivation != GetCVars()->e_ObjectLayersActivation)
  {
    if(GetCVars()->e_ObjectLayersActivation == 2)
      ActivateObjectsLayer(~0, true, true, "ALL_OBJECTS");
    if(GetCVars()->e_ObjectLayersActivation == 3)
      ActivateObjectsLayer(~0, false, true, "ALL_OBJECTS");

    nObjectLayersActivation = GetCVars()->e_ObjectLayersActivation;
  }

  float fNewCVarsSumm = 
      GetCVars()->e_VegetationAlignToTerrainAmount +
      GetCVars()->e_VoxTerHideIntegrated +
			GetCVars()->e_ShadowsCastViewDistRatio +
      GetCVars()->e_Dissolve +
      GetFloatCVar(e_DissolveDistMin) +
      GetFloatCVar(e_DissolveDistMax) +
      GetFloatCVar(e_DissolveDistband) +
      GetCVars()->e_DissolveSpriteDistRatio +
      GetCVars()->e_DissolveSpriteMinDist +
      GetCVars()->e_VegetationUseTerrainColor +
      GetCVars()->e_TerrainDetailMaterials + 
      GetCVars()->e_ViewDistRatio + 
      GetCVars()->e_ViewDistMin + 
      GetCVars()->e_ViewDistRatioDetail + 
      GetCVars()->e_ViewDistRatioVegetation + 
      GetCVars()->e_DefaultMaterial +
      GetCVars()->e_VegetationSpritesDistanceRatio +
      GetCVars()->e_VegetationSpritesDistanceCustomRatioMin +
      GetCVars()->e_VegetationSpritesMinDistance +
      GetGeomDetailScreenRes() +
      GetCVars()->e_Portals +
      GetTerrain()->IsAmbientOcclusionEnabled() +
      GetCVars()->e_DebugDraw +
      GetFloatCVar(e_ViewDistCompMaxSize) + 
    GetCVars()->e_DecalsDefferedStatic + 
		GetRenderer()->GetWidth();

  if( m_fRefreshSceneDataCVarsSumm != -1 && m_fRefreshSceneDataCVarsSumm != fNewCVarsSumm )
  {
    UpdateStatInstGroups();

    // re-register every instance in level
    const float terrainSize = (float)GetTerrainSize();
    GetObjManager()->ReregisterEntitiesInArea(
      Vec3(-terrainSize,-terrainSize,-terrainSize), 
      Vec3(terrainSize*2.f,terrainSize*2.f,terrainSize*2.f));

    // force recreation of terrain meshes
    GetTerrain()->ResetTerrainVertBuffers(NULL,-1);

    // refresh vegetation properties
    UpdateStatInstGroups();

    // force refresh of temporary data associated with visible objects
    MarkRNTmpDataPoolForReset();
  }

  m_fRefreshSceneDataCVarsSumm = fNewCVarsSumm;

  int nRenderTypeEnableCVarSum	= (GetCVars()->e_Vegetation << 0)
    + (GetCVars()->e_Brushes		<< 1)
    +	(GetCVars()->e_Entities		<< 2);

  if(m_nRenderTypeEnableCVarSum != nRenderTypeEnableCVarSum)
  {
    m_nRenderTypeEnableCVarSum = nRenderTypeEnableCVarSum;

    UpdateRenderTypeEnableLookup();
  }

  {
    float fNewCVarsSumm2 = 
      GetCVars()->e_LodRatio;

		if(m_bEditor)
		{
			fNewCVarsSumm2 += float(int(GetSkyColor().GetLength()*10)/10);
		}

    static float fCVarsSumm2 = fNewCVarsSumm2;

    if(fCVarsSumm2 != fNewCVarsSumm2)
    {
      MarkRNTmpDataPoolForReset();

      fCVarsSumm2 = fNewCVarsSumm2;
    }
  }
}

//////////////////////////////////////////////////////////////////////
void C3DEngine::UpdateScene(int nEngineFrameID)
{	
  if(GetCVars()->e_Sun)
    UpdateSunLightSource();

  FindPotentialLightSources(nEngineFrameID);

  // Set traceable fog volume areas
  CFogVolumeRenderNode::SetTraceableArea( AABB( GetCamera().GetPosition(), 1024.0f ) );
}

//////////////////////////////////////////////////////////////////////
void C3DEngine::ShutDown()
{
  if(GetRenderer() != GetSystem()->GetIRenderer())
    CryFatalError("Renderer was deallocated before I3DEngine::ShutDown() call");

  UnlockCGFResources();

  UnloadLevel();

  PrintMessage("PartManager shutdown ...");
  delete m_pPartManager;
  m_pPartManager=0;
  m_pSystem->SetIParticleManager(m_pPartManager);

  PrintMessage("ObjManager shutdown ...");
  delete m_pObjManager;
  m_pObjManager=0;

  if (GetPhysicalWorld())
  {
    CPhysCallbacks::Done();
  }

  // Free the temporary pool's underlying storage
  // and reset the pool
  if (!CTemporaryPool::Shutdown())
  {
    CryFatalError("C3DEngine::Shutdown() could not shutdown temporary pool");
  }

}

#ifdef WIN64
#pragma warning( push )									//AMD Port
#pragma warning( disable : 4311 )
#endif

void C3DEngine::ProcessStreamingLatencyTest(const CCamera & camIn, CCamera & camOut) 
{
  static float fSQTestOffset=0;
  static PodArray<ITexture *> arrTestTextures;
  static ITexture * pTestTexture = 0;
  static ITexture * pLastNotReadyTexture = 0;
  static float fStartTime = 0;
  static float fDelayStartTime = 0;
  static uint32 nMaxTexUsage = 0;

  static int nOpenRequestCount = 0;
  IStreamEngine::SStatistics &stats = gEnv->pSystem->GetStreamEngine()->GetStreamingStatistics();
  if(stats.nOpenRequestCount > nOpenRequestCount)
    nOpenRequestCount = stats.nOpenRequestCount;
  else
    nOpenRequestCount = max(0, nOpenRequestCount + stats.nOpenRequestCount)/2;

  ICVar * pTSFlush = GetConsole()->GetCVar("r_TexturesStreamingDebug");

  if(GetCVars()->e_SQTestBegin == 1)
  { // Init waiting few seconds until streaming is stabilized and all required textures are loaded
    PrintMessage("======== Starting streaming latency test ========");
    fDelayStartTime = GetCurTimeSec();
    nMaxTexUsage = 0;
    GetCVars()->e_SQTestBegin = 2;
    PrintMessage("Waiting %.1f seconds and zero requests and no camera movement", GetCVars()->e_SQTestDelay);

    if(ICVar * pPart = GetConsole()->GetCVar("e_Particles"))
      pPart->Set(0);
    if(ICVar * pAI = GetConsole()->GetCVar("sys_AI"))
      pAI->Set(0);
  }
  else if(GetCVars()->e_SQTestBegin == 2)
  { // Perform waiting 
    if(GetCurTimeSec() - fDelayStartTime > GetCVars()->e_SQTestDelay && !nOpenRequestCount && m_fAverageCameraSpeed<.01f)
    {
      pTSFlush->Set(0);
      GetCVars()->e_SQTestBegin = 3;
    }
    else
      pTSFlush->Set(3);
  }
  else if(GetCVars()->e_SQTestBegin == 3)
  { // Build a list of all important loaded textures
    PrintMessage("Collect information about loaded textures");

    fSQTestOffset = (float)GetCVars()->e_SQTestDistance;

    arrTestTextures.Clear();
    int nTexCount = *(int*)GetRenderer()->EF_Query(EFQ_GetAllTextures,0);
    if (nTexCount > 0)
    {
      ITexture **pTextures = new ITexture*[nTexCount];
      GetRenderer()->EF_Query( EFQ_GetAllTextures,(INT_PTR)pTextures );
      for (int i = 0; i < nTexCount; i++)
      {
        ITexture *pTexture = pTextures[i];
        if(pTexture->GetAccessFrameId() > (int)(GetMainFrameID()-4))
          if(pTexture->GetMinLoadedMip() <= GetCVars()->e_SQTestMip)
            if(pTexture->IsStreamable())
              if(pTexture->GetWidth()*pTexture->GetHeight() >= 256*256)
              {
                arrTestTextures.Add(pTexture);

                if(strstr(pTexture->GetName(), GetCVars()->e_SQTestTextureName->GetString()))
                {
                  pTestTexture = pTexture;
                  PrintMessage("Test texture name: %s", pTexture->GetName());
                }
              }
      }
      delete [] pTextures;
    }
    PrintMessage("%d test textures found", arrTestTextures.Count());

    PrintMessage("Teleporting camera to offset position");

    GetCVars()->e_SQTestBegin = 4;
  }
  else if(GetCVars()->e_SQTestBegin == 4)
  { // Init waiting few seconds until streaming is stabilized and all required textures are loaded
    fDelayStartTime = GetCurTimeSec();
    GetCVars()->e_SQTestBegin = 5;
    PrintMessage("Waiting %.1f seconds and zero requests and no camera movement", GetCVars()->e_SQTestDelay);
  }
  else if(GetCVars()->e_SQTestBegin == 5)
  { // Move camera to offset position and perform waiting
    Matrix34 mat = camIn.GetMatrix();
    Vec3 vPos = camIn.GetPosition() - camIn.GetViewdir()*fSQTestOffset;
    mat.SetTranslation(vPos);
    camOut.SetMatrix(mat);

    if(GetCurTimeSec() - fDelayStartTime > GetCVars()->e_SQTestDelay && !nOpenRequestCount && m_fAverageCameraSpeed<.01f)
    {
      PrintMessage("Begin camera movement");
      GetCVars()->e_SQTestBegin = 6;
      pTSFlush->Set(0);
    }
    else
      pTSFlush->Set(3);
  }
  else if(GetCVars()->e_SQTestBegin == 6)
  { // Process camera movement from offset position to test point
    Matrix34 mat = camIn.GetMatrix();
    Vec3 vPos = camIn.GetPosition() - camIn.GetViewdir()*fSQTestOffset;
    mat.SetTranslation(vPos);
    camOut.SetMatrix(mat);

    fSQTestOffset -= GetTimer()->GetFrameTime()*(float)GetCVars()->e_SQTestMoveSpeed;

    STextureStreamingStats statsTex(true);
    m_pRenderer->EF_Query(EFQ_GetTexStreamingInfo, (INT_PTR)&statsTex);
    nMaxTexUsage = max(nMaxTexUsage, statsTex.nRequiredTexturesSize);

    if(fSQTestOffset<=0)
    {
      PrintMessage("Finished camera movement");
      fStartTime = GetCurTimeSec();
      PrintMessage("Waiting for %d textures to stream in ...", arrTestTextures.Count());

      GetCVars()->e_SQTestBegin = 7;
      pLastNotReadyTexture = 0;
    }
  }
  else if(GetCVars()->e_SQTestBegin == 7)
  { // Wait until test all needed textures are loaded again

    STextureStreamingStats statsTex(true);
    m_pRenderer->EF_Query(EFQ_GetTexStreamingInfo, (INT_PTR)&statsTex);
    nMaxTexUsage = max(nMaxTexUsage, statsTex.nRequiredTexturesSize);

    if(pTestTexture)
    {
      if(pTestTexture->GetMinLoadedMip() <= GetCVars()->e_SQTestMip)
      {
        PrintMessage("BINGO: Selected test texture loaded in %.1f sec", GetCurTimeSec() - fStartTime);
        pTestTexture = NULL;
        if(!arrTestTextures.Count())
        {
          GetCVars()->e_SQTestBegin = 0;
          GetConsole()->GetCVar("e_SQTestBegin")->Set(0);
        }
      }
    }

    if(arrTestTextures.Count())
    {
      int nFinishedNum = 0;
      for (int i = 0; i < arrTestTextures.Count(); i++)
      {
        if(arrTestTextures[i]->GetMinLoadedMip() <= GetCVars()->e_SQTestMip)
          nFinishedNum++;
        else
          pLastNotReadyTexture = arrTestTextures[i];
      }

      if(nFinishedNum == arrTestTextures.Count())
      {
        PrintMessage("BINGO: %d of %d test texture loaded in %.1f sec", nFinishedNum, arrTestTextures.Count(), GetCurTimeSec() - fStartTime);
        if(pLastNotReadyTexture)
          PrintMessage("LastNotReadyTexture: %s [%d x %d]", pLastNotReadyTexture->GetName(), pLastNotReadyTexture->GetWidth(), pLastNotReadyTexture->GetHeight());
        PrintMessage("MaxTexUsage: %d MB", nMaxTexUsage/1024/1024);
        arrTestTextures.Clear();

        GetCVars()->e_SQTestBegin = 0;
        GetConsole()->GetCVar("e_SQTestBegin")->Set(0);

        m_arrProcessStreamingLatencyTestResults.Add(GetCurTimeSec() - fStartTime);
        m_arrProcessStreamingLatencyTexNum.Add(nFinishedNum);

        if(GetCVars()->e_SQTestCount == 0)
        {
          const char * szTestResults = "TestResults";
          char path[ICryPak::g_nMaxPath]="";
          gEnv->pCryPak->AdjustFileName(string(string(szTestResults) + "\\" + "Streaming_Latency_Test.xml").c_str(), path, ICryPak::FLAGS_PATH_REAL | ICryPak::FLAGS_FOR_WRITING);
          gEnv->pCryPak->MakeDir( szTestResults );

          if(FILE * f = ::fopen(path, "wb"))
          {
            float fAverTime = 0;
            for(int i=0; i<m_arrProcessStreamingLatencyTestResults.Count(); i++)
              fAverTime += m_arrProcessStreamingLatencyTestResults[i];
            fAverTime/=m_arrProcessStreamingLatencyTestResults.Count();

            int nAverTexNum = 0;
            for(int i=0; i<m_arrProcessStreamingLatencyTexNum.Count(); i++)
              nAverTexNum += m_arrProcessStreamingLatencyTexNum[i];
            nAverTexNum/=m_arrProcessStreamingLatencyTexNum.Count();

            fprintf(f,
              "<phase name=\"Streaming_Latency_Test\">\n"
              "<metrics name=\"Streaming\">\n"
              "<metric name=\"AvrLatency\" value=\"%.1f\"/>\n"
              "<metric name=\"AvrTexNum\" value=\"%d\"/>\n"
              "</metrics>\n"
              "</phase>\n", 
              fAverTime,
              nAverTexNum);

            ::fclose(f);
          }

          if(GetCVars()->e_SQTestExitOnFinish)
            GetSystem()->Quit();
        }
      }
      else if((GetMainFrameID()&31)==0)
      {
        PrintMessage("Waiting: %d of %d test texture loaded in %.1f sec", nFinishedNum, arrTestTextures.Count(), GetCurTimeSec() - fStartTime);
      }
    }
  }
}

//////////////////////////////////////////////////////////////////////
bool C3DEngine::SetupCamera(const CCamera & _newCam, const char * szCallerName) 
{
  if(IsCameraAnd3DEngineInvalid(_newCam, szCallerName))
    return false;

	CCamera newCam = _newCam;

	// disabled this hack for now - breaks few functionality relying on caching projection matrix
	//static ICVar*	pVarTesselation = GetConsole()->GetCVar("r_Tesselation");
	//if (pVarTesselation && pVarTesselation->GetIVal()>0)
	//{
	//	int camX = newCam.GetViewSurfaceX();
	//	int camZ = newCam.GetViewSurfaceZ();
	//	f32 camFov = newCam.GetFov();
	//	f32 camNearPlane = newCam.GetNearPlane();
	//	f32 camFarPlane = newCam.GetFarPlane();
	//	float camPixelAspectRatio = newCam.GetPixelAspectRatio();
	//	camNearPlane/=2.0f;
	//	newCam.SetFrustum(camX, camZ, camFov, camNearPlane, camFarPlane, camPixelAspectRatio);
	//}

	{ // scene scanner

		static int nScanStage = -1;
		static Matrix34 matInit;

		bool bHit = (GetMainFrameID()&7)==7;

		if(GetCVars()->e_VoxScanScene && bHit && nScanStage==-1)
		{
			PrintMessage("Init scene scanning");

			static bool bHack = true;
			if(bHack)
			{
				ScanScene(0);
				m_sceneFrustums.Clear();
				bHack = false;
			}

			matInit = GetCamera().GetMatrix();
			nScanStage = 0;

			GetCVars()->e_VoxScanScene = 0;
			bHit = false;

			GetCVars()->e_VegetationSprites = 0;
			GetCVars()->e_Particles = 0;

			GetConsole()->GetCVar("r_DisplayInfo")->Set(0);
			GetConsole()->GetCVar("r_PostProcessEffects")->Set(0);
			GetConsole()->GetCVar("r_SSReflections")->Set(0);
		}

		if(nScanStage>=0)
		{
			Matrix34 mat; mat.SetIdentity();
			mat.SetTranslation(matInit.GetTranslation());

			Matrix33 matRot; 

			if(nScanStage>=0 && nScanStage<=3)
			{
				matRot.SetRotationZ(float(g_PI2/4)*nScanStage);
			}
			else if(nScanStage == 4)
			{
				matRot.SetRotationVDir(Vec3(0,0,1));
			}
			else if(nScanStage == 5)
			{
				matRot.SetRotationVDir(Vec3(0,0,-1));
			}

			newCam.SetMatrix(mat*matRot);

			SetCamera(newCam);
			GetRenderer()->SetCamera(newCam);

			if(bHit)
			{
				PrintMessage("Scanning view %d", nScanStage);
				ScanScene(nScanStage);
				nScanStage++;
				if(nScanStage>5)
					nScanStage = -1;
			}
		}
	}

  //newCam.SetFrustum(newCam.GetViewSurfaceX(),newCam.GetViewSurfaceZ(), DEG2RAD(45.0f), newCam.GetNearPlane(), newCam.GetFarPlane(), newCam.GetPixelAspectRatio() );

  if(GetFloatCVar(e_CameraRotationSpeed))
  {
    Matrix34 mat = _newCam.GetMatrix();
    Matrix33 matRot; matRot.SetRotationZ(-GetCurTimeSec()*GetFloatCVar(e_CameraRotationSpeed));
    newCam.SetMatrix(mat*matRot);
  }

#if !defined(_RELEASE)
  {
    //this feature move the camera along with the player to a certain position and sets the angle accordingly 
    //	(does not work via goto)
    //u can switch it off again via e_CameraGoto 0
    const char* const pCamGoto = GetCVars()->e_CameraGoto->GetString();
    assert(pCamGoto);
    if(strlen(pCamGoto) > 1)
    {
      Ang3 aAngDeg;
      Vec3 vPos;
      int args = sscanf(pCamGoto, "%f %f %f %f %f %f",&vPos.x,&vPos.y,&vPos.z,&aAngDeg.x,&aAngDeg.y,&aAngDeg.z);
      if(args >= 3)
      {
        Vec3 curPos = newCam.GetPosition();
        if(fabs(vPos.x - curPos.x) > 10.f || fabs(vPos.y - curPos.y) > 10.f || fabs(vPos.z - curPos.z) > 10.f)
        {
          char buf[128];
          sprintf(buf, "goto %f %f %f", vPos.x,vPos.y,vPos.z);
          gEnv->pConsole->ExecuteString(buf);
        }
        if(args >= 6)
        {
          Matrix34 mat = _newCam.GetMatrix();
          mat.SetTranslation(vPos);
          mat.SetRotation33( Matrix33::CreateRotationXYZ(DEG2RAD(aAngDeg)) );
          newCam.SetMatrix(mat);
        }
      }
    }
  }

  // Streaming latency test
  if(GetCVars()->e_SQTestCount && !GetCVars()->e_SQTestBegin)
  {
    GetConsole()->GetCVar("e_SQTestBegin")->Set(1);
    GetConsole()->GetCVar("e_SQTestCount")->Set(GetCVars()->e_SQTestCount-1);
  }
  if(GetCVars()->e_SQTestBegin)
    ProcessStreamingLatencyTest(_newCam, newCam);

#endif

	if(GetCVars()->e_CameraFreeze == 0 && GetCVars()->e_CoverageBufferDebugFreeze == 0)
	  SetCamera(newCam);
  else
	{
    DebugDraw_PushFrustrum( "MainCamera", GetCamera(), Col_Green, -999.f );
		DrawSphere(GetCamera().GetPosition(), .05f);
	}

  m_bCameraUnderWater = IsUnderWater(newCam.GetPosition());

  GetRenderer()->SetCamera(newCam);

  return true;
}

#ifdef WIN64
#pragma warning( pop )									//AMD Port
#endif

IStatObj* C3DEngine::LoadStatObj( const char *szFileName,const char *szGeomName,IStatObj::SSubObject **ppSubObject, bool bUseStreaming,unsigned long nLoadingFlags )
{
  if(!szFileName || !szFileName[0])
  {
    m_pSystem->Warning( VALIDATOR_MODULE_3DENGINE,VALIDATOR_ERROR,0,0,"I3DEngine::LoadStatObj: filename is not specified" );
    return 0;
  }

  if(!m_pObjManager)
    m_pObjManager = new CObjManager();

  return m_pObjManager->LoadStatObj(szFileName, szGeomName, ppSubObject, bUseStreaming,nLoadingFlags);
}

IStatObj * C3DEngine::FindStatObjectByFilename(const char * filename)
{
  if ( filename ==  NULL )
    return NULL;

  if ( filename[0] == 0 )
    return NULL;

  return m_pObjManager->FindStaticObjectByFilename(filename);
}

Vec3 C3DEngine::GetEntityRegisterPoint( IRenderNode* pEnt )
{
  AABB aabb = pEnt->GetBBox();

  Vec3 vPoint;

  if(pEnt->m_dwRndFlags & ERF_REGISTER_BY_POSITION)
  {
    vPoint = pEnt->GetPos();  
    vPoint.z += 0.25f;

    if(pEnt->GetRenderNodeType() != eERType_Light)
    {
      // check for valid position
      if(aabb.GetDistanceSqr(vPoint)>sqr(128.f))
        Warning("I3DEngine::RegisterEntity: invalid entity position: Name: %s, Class: %s, Pos=(%.1f,%.1f,%.1f), BoxMin=(%.1f,%.1f,%.1f), BoxMax=(%.1f,%.1f,%.1f)", 
        pEnt->GetName(), pEnt->GetEntityClassName(), 
        pEnt->GetPos().x, pEnt->GetPos().y, pEnt->GetPos().z,
        pEnt->GetBBox().min.x, pEnt->GetBBox().min.y, pEnt->GetBBox().min.z,
        pEnt->GetBBox().max.x, pEnt->GetBBox().max.y, pEnt->GetBBox().max.z
        );

      // clamp by bbox
      vPoint.CheckMin(aabb.max);
      vPoint.CheckMax(aabb.min+Vec3(0,0,.5f));

			//// hack for case when 1p body crossing horizontal portal
			//// camera is in top area but body is set by game to be registered in bottom area
			//// normally such object should be set to ALWAYS_VISIBLE and this hack will be not needed in this case
			//if(pEnt->GetRenderNodeType() == eERType_RenderProxy)
			//{
			//	float fCamZ = GetCamera().GetPosition().z;
			//	if( vPoint.z < min(aabb.min.z+.75f, fCamZ) )
			//		if( strcmp(pEnt->GetName(), "Dude") == 0 )
			//			vPoint.z = min(aabb.min.z+.75f, fCamZ);
			//}
			}
    }
  else
    vPoint = aabb.GetCenter();  

  return vPoint;
}

void C3DEngine::RegisterEntity( IRenderNode* pEnt, int nSID, int nSIDConsideredSafe )
{
  FUNCTION_PROFILER_3DENGINE;

  if ( gEnv->mMainThreadId != CryGetCurrentThreadId() )
  {
    CryFatalError( "CRNTmpData should only be allocated and free'd on main thread." );
  }

  if(m_nRenderStackLevel>=0)
  {
    Warning("I3DEngine::RegisterEntity: RegisterEntity is called for %s during 3DEngine::Render() call - ignored", pEnt->GetName());
    return;
  }


#ifdef _DEBUG // crash test basically
  const char * szClass = pEnt->GetEntityClassName();
  const char * szName = pEnt->GetName();
  if(!szName[0] && !szClass[0])
    Warning("I3DEngine::RegisterEntity: Entity undefined"); // do not register undefined objects
  //  if(strstr(szName,"Dude"))
  //  int y=0;
#endif

  AABB aabb(pEnt->GetBBox());
  float fObjRadiusSqr = aabb.GetRadiusSqr();
  EERType eERType = pEnt->GetRenderNodeType();

#ifdef SUPP_HMAP_OCCL  
  if(pEnt->m_pRNTmpData)
    pEnt->m_pRNTmpData->userData.m_OcclState.vLastVisPoint.Set(0,0,0);
#endif
  if(m_pVoxTerrain && (eERType == eERType_Decal || eERType == eERType_Road))
    m_pVoxTerrain->RequestTextureUpdate(aabb);

  const unsigned int dwRndFlags = pEnt->GetRndFlags();

  if(!(dwRndFlags&ERF_RENDER_ALWAYS) && !(dwRndFlags&ERF_CASTSHADOWMAPS))
    if(GetCVars()->e_ObjFastRegister && pEnt->m_pOcNode && ((COctreeNode*)pEnt->m_pOcNode)->IsRightNode(aabb, fObjRadiusSqr, pEnt->m_fWSMaxViewDist))
    { // same octree node
      Vec3 vEntCenter = GetEntityRegisterPoint( pEnt );

      IVisArea * pVisArea = pEnt->GetEntityVisArea();
      if(pVisArea && pVisArea->IsPointInsideVisArea(vEntCenter))
        return; // same visarea

      IVisArea * pVisAreaFromPos = (!m_pVisAreaManager || dwRndFlags&ERF_OUTDOORONLY) ? NULL : GetVisAreaManager()->GetVisAreaFromPos(vEntCenter);
      if(pVisAreaFromPos == pVisArea)
        return; // same visarea or same outdoor
    }

    if(pEnt->m_pOcNode)
    {
      UnRegisterEntity( pEnt );
    }
    else if(GetCVars()->e_StreamCgf && eERType == eERType_RenderProxy)
    { //  Temporary solution: Force streaming priority update for objects that was not registered before 
      //  and was not visible before since usual prediction system was not able to detect them

      if(pEnt->GetDrawFrame(0) < Cry3DEngineBase::GetFrameID()-16 || (pEnt->GetRndFlags() & ERF_HUD))
      {
        const Vec3& vCamPos = GetCamera().GetPosition();
        float fEntDistance = cry_sqrtf(Distance::Point_AABBSq(vCamPos,aabb))*m_fZoomFactor;
        GetObjManager()->UpdateRenderNodeStreamingPrioriry(pEnt, fEntDistance, false);
        if(GetCVars()->e_StreamCgfDebug==2)
          PrintMessage("C3DEngine::RegisterEntity__GetObjManager()->UpdateRenderNodeStreamingPrioriry %s", pEnt->GetName());
      }
    }

    if(eERType == eERType_Vegetation)
    {
      CVegetation * pInst = (CVegetation*)pEnt;
      pInst->UpdateRndFlags();
    }

    pEnt->m_fWSMaxViewDist = pEnt->GetMaxViewDist();

    if(eERType != eERType_Light)
    {
      if(fObjRadiusSqr > sqr(MAX_VALID_OBJECT_VOLUME) || !_finite(fObjRadiusSqr))
      {
        Warning("I3DEngine::RegisterEntity: Object has invalid bbox: name: %s, class name: %s, GetRadius() = %.2f",
          pEnt->GetName(), pEnt->GetEntityClassName(), fObjRadiusSqr);
        return; // skip invalid objects - usually only objects with invalid very big scale will reach this point
      }

      if(dwRndFlags & ERF_RENDER_ALWAYS)
      {
        if(m_lstAlwaysVisible.Find(pEnt)<0)
          m_lstAlwaysVisible.Add(pEnt);

        if (dwRndFlags & ERF_HUD)
          return;
      }
    }
    else
    {
      CLightEntity * pLight = (CLightEntity *)pEnt;
      if( (pLight->m_light.m_Flags & (DLF_IGNORES_VISAREAS|DLF_DEFERRED_LIGHT|DLF_THIS_AREA_ONLY)) == (DLF_IGNORES_VISAREAS|DLF_DEFERRED_LIGHT))
      {
        if(m_lstAlwaysVisible.Find(pEnt)<0)
          m_lstAlwaysVisible.Add(pEnt);
      }
    }

	//////////////////////////////////////////////////////////////////////////
	// Check for occlusion proxy.
	{
    CStatObj * pStatObj = (CStatObj *)pEnt->GetEntityStatObj();
		if(pStatObj)
		{
			if(pStatObj->m_bHaveOcclusionProxy)
			{
				pEnt->m_dwRndFlags |=	ERF_GOOD_OCCLUDER;
				pEnt->m_nInternalFlags |=	IRenderNode::HAS_OCCLUSION_PROXY;
			}
		}
	}
	//////////////////////////////////////////////////////////////////////////
	if(pEnt->m_dwRndFlags&ERF_OUTDOORONLY || !(m_pVisAreaManager && m_pVisAreaManager->SetEntityArea(pEnt, aabb, fObjRadiusSqr)))
	{
#ifndef SEG_WORLD
		if(nSID == -1)
		{ 
			nSID = 0;

			// if segment is not set - find best one automatically
			Vec3 vCenter = aabb.GetCenter();
			const int iTreeCount = Get3DEngine()->m_pObjectsTree.Count();
			for(int n=0; n < iTreeCount; n++)
			{
				if(Get3DEngine()->m_pObjectsTree[n])
				{
					if(Overlap::Point_AABB2D(vCenter, m_pObjectsTree[n]->GetNodeBox()))
					{
						nSID = n;
						break;
					}
				}
			}
		}

      if(nSID>=0 && nSID<m_pObjectsTree.Count())
      {
        if(!m_pObjectsTree[nSID])
        {
          const float terrainSize = (float)GetTerrainSize();
          MEMSTAT_CONTEXT(EMemStatContextTypes::MSC_Terrain, EMemStatContextFlags::MSF_Instance, "Octree node");
          m_pObjectsTree[nSID] = new COctreeNode(nSID, AABB(Vec3(0,0,0),Vec3(terrainSize, terrainSize, terrainSize)), NULL);
        }

        m_pObjectsTree[nSID]->InsertObject(pEnt, aabb, fObjRadiusSqr, aabb.GetCenter());
	  }
#else
		if(eERType != eERType_Vegetation)
		{
			if(GetITerrain())
			{ 
				Vec3 vCenter = aabb.GetCenter();
				nSID = GetITerrain()->WorldToSegment(vCenter, -1);
			}
		}
		else
		{
			// use specified sid from which the objects are actually serialized for vegetation objects
			CVegetation *pInst = (CVegetation *)pEnt;
			nSID = pInst->m_nStaticTypeSlot;
		}

      if(nSID>=0 && nSID<m_pObjectsTree.Count())
	  {
		  // TODO: this should only work if the segment is safe or if it's unsafe and the object is being inserted by SetCompiledData...
		  if (nSID == nSIDConsideredSafe || IsSegmentSafeToUse(nSID))
			  m_pObjectsTree[nSID]->InsertObject(pEnt, aabb, fObjRadiusSqr, aabb.GetCenter());
      }
#endif
    }

    if(pEnt->GetIntegrationType()>=eIT_VoxelMesh && GetTerrain() && m_bEditor)
    {
      const AABB box(pEnt->GetBBox());
      GetTerrain()->ResetTerrainVertBuffers(&box,nSID);
    }

    // register decals, to clean up longer not renderes decals and their render meshes
    if( eERType == eERType_Decal )
    {
      m_decalRenderNodes.push_back( (IDecalRenderNode*)pEnt);
    }
}

bool C3DEngine::UnRegisterEntity( IRenderNode* pEnt )
{
  FUNCTION_PROFILER_3DENGINE;

  if ( gEnv->mMainThreadId != CryGetCurrentThreadId() )
  {
    CryFatalError( "CRNTmpData should only be allocated and free'd on main thread." );
  }

  //  if(!(m_pObjectsTree[nSID] || m_pVisAreaManager))
  //  return false;

  //	assert(m_nRenderStackLevel<0);
  //	if(m_nRenderStackLevel>=0)
  	//	Warning("C3DEngine::UnRegisterEntity: UnRegisterEntity is called for %s during 3DEngine::Render() call", pEnt->GetName());

#ifdef _DEBUG // crash test basically
  const char * szClass = pEnt->GetEntityClassName();
  const char * szName = pEnt->GetName();
  if(!szName[0] && !szClass[0])
    Warning("C3DEngine::RegisterEntity: Entity undefined");
#endif

  EERType eRenderNodeType = pEnt->GetRenderNodeType();

  bool bFound = false;
  //pEnt->PhysicalizeFoliage(false);

  if(pEnt->m_pOcNode)
    bFound = ((COctreeNode*)pEnt->m_pOcNode)->DeleteObject(pEnt);

  if(pEnt->m_dwRndFlags & ERF_RENDER_ALWAYS || (eRenderNodeType == eERType_Light))
    m_lstAlwaysVisible.Delete(pEnt);

	{
		// make sure object is not added to render list
		PodArray<SRenderMeshRenderInfo> &rList = GetObjManager()->m_arrSRenderMeshRenderList_RenderObject;
		for(int i=0; i<rList.Count(); i++)
		{
			if(rList[i].pEnt == pEnt)
			{
				Warning("C3DEngine::UnRegisterEntity: Deleting of object when it is already added into rendering pipeline");
				GetObjManager()->m_arrSRenderMeshRenderList_RenderObjectData.Delete(i);
				rList.Delete(i);
				i--;
			}
		}
	}

  if(m_pVoxTerrain && (eRenderNodeType == eERType_Decal || eRenderNodeType == eERType_Road))
    m_pVoxTerrain->RequestTextureUpdate(pEnt->GetBBox());

  if(pEnt->GetIntegrationType()>=eIT_VoxelMesh && GetTerrain() && m_bEditor)
  {
    const AABB box(pEnt->GetBBox());
    GetTerrain()->ResetTerrainVertBuffers(&box,pEnt->m_nSID);
  }

  if( eRenderNodeType == eERType_Decal )
  {
    std::vector<IDecalRenderNode*>::iterator it = std::find(m_decalRenderNodes.begin(), m_decalRenderNodes.end(), (IDecalRenderNode*)pEnt );
    if( it != m_decalRenderNodes.end() )
    {
      m_decalRenderNodes.erase(it);
    }
  }

  return bFound;
}

bool C3DEngine::IsThereFreeMemoryForNotImportantStuff()
{
















  return true;
}

bool C3DEngine::CreateDecalInstance( const CryEngineDecalInfo& decal, CDecal* pCallerManagedDecal )
{
  if( !GetCVars()->e_Decals && !pCallerManagedDecal )
    return false;

  return m_pDecalManager->Spawn( decal, pCallerManagedDecal );
}

void C3DEngine::SelectEntity(IRenderNode *pEntity)
{
  static IRenderNode *pSelectedNode; 
  static float fLastTime;
  if (pEntity && GetCVars()->e_Decals == 3)
  {
    float fCurTime = gEnv->pTimer->GetAsyncCurTime();
    if (fCurTime-fLastTime < 1.0f)
      return;
    fLastTime = fCurTime;
    if (pSelectedNode)
      pSelectedNode->SetRndFlags(ERF_SELECTED, false);
    pEntity->SetRndFlags(ERF_SELECTED, true);
    pSelectedNode = pEntity;
  }
}

void C3DEngine::CreateDecal(const struct CryEngineDecalInfo & decal)
{
  IF(!GetCVars()->e_DecalsAllowGameDecals, 0)
    return;

  if(GetCVars()->e_Decals == 2)
  {
    IRenderNode * pRN = decal.ownerInfo.pRenderNode;
    PrintMessage("Debug: C3DEngine::CreateDecal: Pos=(%.1f,%.1f,%.1f) Size=%.2f DecalMaterial=%s HitObjectName=%s(%s)", 
      decal.vPos.x, decal.vPos.y, decal.vPos.z, decal.fSize, decal.szMaterialName,
      pRN ? pRN->GetName() : "NULL", pRN ? pRN->GetEntityClassName() : "NULL");
  }

  if(m_pVoxTerrain && !decal.bForceSingleOwner)
  {
    CDecalRenderNode * pDecal = (CDecalRenderNode *)CreateRenderNode(eERType_Decal);

    IMaterial * pMat = GetMaterialManager()->LoadMaterial(decal.szMaterialName);
    pDecal->SetMaterial(pMat);

    Matrix33 matRot; matRot.SetIdentity();

    Matrix33 matRot0 = Matrix33::CreateRotationVDir( (decal).vNormal, (decal).fAngle );

    matRot = Matrix33::CreateRotationX(-gf_PI/2);
    Matrix33 matRotZ = Matrix33::CreateRotationZ(gf_PI/2);

    matRot = matRot0*matRot;
    matRot = matRot * matRotZ;

    Matrix34 wtm; wtm.SetIdentity();
    wtm.SetScale(Vec3(decal.fSize,decal.fSize,decal.fSize), decal.vPos);
    wtm = wtm * matRot;

    SDecalProperties decalProperties;
    decalProperties.m_projectionType = SDecalProperties::ePlanar;

    decalProperties.m_pos = wtm.TransformPoint( Vec3( 0, 0, 0 ) );
    decalProperties.m_normal = wtm.TransformVector( Vec3( 0, 0, 1 ) );
    decalProperties.m_pMaterialName = pMat->GetName();
    decalProperties.m_radius = decalProperties.m_normal.GetLength();

    Matrix33 rotation( wtm ); 
    rotation.SetRow( 0, rotation.GetRow( 0 ).GetNormalized() );
    rotation.SetRow( 1, rotation.GetRow( 1 ).GetNormalized() );
    rotation.SetRow( 2, rotation.GetRow( 2 ).GetNormalized() );

    decalProperties.m_explicitRightUpFront = rotation;
    decalProperties.m_sortPrio = 32;

    pDecal->SetDecalProperties( decalProperties );

    pDecal->SetMatrix( wtm );

    pDecal->SetDecalProperties( decalProperties );

    RegisterEntity(pDecal);

    return;
  }

  // decal on CHR, ownerInfo.nPartID is bone id
  if(decal.ownerInfo.pRenderNode)
	{
    if(ICharacterInstance * pChar = decal.ownerInfo.pRenderNode->GetEntityCharacter(0))
    {
      if(pChar->GetOjectType() == CHR)
      {
        CryEngineDecalInfo DecalLCS = decal;
        pChar->CreateDecal(DecalLCS);
				return;
      }
    }
	}

  assert(!decal.pExplicitRightUpFront); // only game-play decals come here

  static uint32 nGroupId = 0; nGroupId++;

  if((GetCVars()->e_DecalsDefferedStatic == 1 && decal.pExplicitRightUpFront) || 
    (GetCVars()->e_DecalsDefferedDynamic == 1 && !decal.pExplicitRightUpFront && 
    (!decal.ownerInfo.pRenderNode || decal.ownerInfo.pRenderNode->GetRenderNodeType() == eERType_Brush || decal.fGrowTimeAlpha || decal.fSize>GetFloatCVar(e_DecalsDefferedDynamicMinSize))) 
    && !decal.bForceSingleOwner)
  {
    CryEngineDecalInfo decal_adjusted = decal;
    decal_adjusted.nGroupId = nGroupId;
    decal_adjusted.bDeferred = true;
    m_pDecalManager->SpawnHierarchical( decal_adjusted, NULL );
    return;
  }

	if(!decal.bForceSingleOwner && (decal.fSize > GetFloatCVar(e_DecalsDefferedDynamicMinSize)) && decal.ownerInfo.pRenderNode->GetRenderNodeType() != eERType_RenderProxy)
  {
    PodArray<SRNInfo> lstEntities;
    Vec3 vRadius(decal.fSize,decal.fSize,decal.fSize);
    const AABB cExplosionBox(decal.vPos - vRadius, decal.vPos + vRadius);

		if(CVisArea* pArea = (decal.ownerInfo.pRenderNode ? (CVisArea*)decal.ownerInfo.pRenderNode->GetEntityVisArea() : NULL))
    {
      if(pArea->m_pObjectsTree)
        pArea->m_pObjectsTree->MoveObjectsIntoList(&lstEntities, &cExplosionBox, false, true, true, true);
    }
    else
      Get3DEngine()->MoveObjectsIntoListGlobal(&lstEntities, &cExplosionBox, false, true, true, true);

		SRNInfo sri(decal.ownerInfo.pRenderNode);
		if(lstEntities.Find(sri)<0)
			lstEntities.Add(sri);

    for(int i=0; i<lstEntities.Count(); i++)
    {
      // decals on statobj's of render node
      CryEngineDecalInfo decalOnRenderNode = decal;
      decalOnRenderNode.ownerInfo.pRenderNode = lstEntities[i].pNode;
      decalOnRenderNode.nGroupId = nGroupId;

      //      if(decalOnRenderNode.ownerInfo.pRenderNode->GetRenderNodeType() != decal.ownerInfo.pRenderNode->GetRenderNodeType())
      //      continue;

      if(decalOnRenderNode.ownerInfo.pRenderNode->GetRndFlags()&ERF_HIDDEN)
        continue;

      m_pDecalManager->SpawnHierarchical( decalOnRenderNode, NULL );
    }
  }
  else
  {
    CryEngineDecalInfo decalStatic = decal;
    decalStatic.nGroupId = nGroupId;
    m_pDecalManager->SpawnHierarchical( decalStatic, NULL );
  }
}

/*
float C3DEngine::GetDayTime(float fPrevDayTime) 
{ 
if(fPrevDayTime)
{
float fDayTimeDiff = (GetCVars()->e_day_time - fPrevDayTime);
while(fDayTimeDiff>12)
fDayTimeDiff-=12;
while(fDayTimeDiff<-12)
fDayTimeDiff+=12;  
fDayTimeDiff = (float)fabs(fDayTimeDiff);
return fDayTimeDiff;
}

return GetCVars()->e_day_time; 
} */

namespace
{
  inline const float GetSunSkyRel(const Vec3& crSkyColor, const Vec3& crSunColor)
  {
    const float cSunMax = max(0.1f, max(crSunColor.x, max(crSunColor.y, crSunColor.z)));
    const float cSkyMax = max(0.1f, max(crSkyColor.x, max(crSkyColor.y, crSkyColor.z)));
    return cSunMax / (cSkyMax + cSunMax);
  }
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetSkyColor(Vec3 vColor)
{
  if (m_pObjManager)
  {
    m_pObjManager->m_vSkyColor = vColor;
    m_pObjManager->m_fSunSkyRel = GetSunSkyRel(m_pObjManager->m_vSkyColor, m_pObjManager->m_vSunColor);
  }
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetSunColor(Vec3 vColor)
{
  if (m_pObjManager)
  {
    m_pObjManager->m_vSunColor = vColor;
    m_pObjManager->m_fSunSkyRel = GetSunSkyRel(m_pObjManager->m_vSkyColor, m_pObjManager->m_vSunColor);
  }
}

void C3DEngine::SetSkyBrightness(float fMul)
{
  if (m_pObjManager)
    m_pObjManager->m_fSkyBrightMul = fMul;
}

void C3DEngine::SetSSAOAmount(float fMul)
{
  if (m_pObjManager)
    m_pObjManager->m_fSSAOAmount = fMul;
}

void C3DEngine::SetSSAOContrast(float fMul)
{
  if (m_pObjManager)
    m_pObjManager->m_fSSAOContrast = fMul;
}

void C3DEngine::SetGIAmount(float fMul)
{
  if (m_pObjManager)
    m_pObjManager->m_fGIAmount = fMul;
}

float C3DEngine::GetTerrainElevation(float x, float y, bool bIncludeOutdoorVoxles, int nSID)
{
  if(GetCVars()->e_VoxTerHeightmapEditing && m_pVoxTerrain && GetCVars()->e_VoxTer && C3DEngine::m_pGetLayerIdAtCallback)
  {
    float fTerrainZ = C3DEngine::m_pGetLayerIdAtCallback->GetElevationAtPosition(y/GetTerrain()->GetHeightMapUnitSize(),x/GetTerrain()->GetHeightMapUnitSize());
    return fTerrainZ;
  }

  float fZ = 0;

  if(m_pTerrain)
    fZ = m_pTerrain->GetZApr(x, y, bIncludeOutdoorVoxles, nSID);

  Vec3 vNorm;
  if(m_pVoxTerrain && GetCVars()->e_VoxTer && m_bEditor)
  {
    if(bIncludeOutdoorVoxles)
      fZ = max(0.f, m_pVoxTerrain->GetElevation3D(Vec3(x,y,0), vNorm));
    else
      fZ = max(fZ, m_pVoxTerrain->GetElevation3D(Vec3(x,y,0), vNorm));
  }

  return fZ;
}

float C3DEngine::GetTerrainElevation3D(Vec3 vPos)
{
  if(GetCVars()->e_VoxTerHeightmapEditing && m_pVoxTerrain && GetCVars()->e_VoxTer && C3DEngine::m_pGetLayerIdAtCallback)
  {
    float fTerrainZ = C3DEngine::m_pGetLayerIdAtCallback->GetElevationAtPosition(vPos.y/GetTerrain()->GetHeightMapUnitSize(),vPos.x/GetTerrain()->GetHeightMapUnitSize());
    return fTerrainZ;
  }

  float fZ = 0;

  if(m_pTerrain)
    fZ = m_pTerrain->GetZApr(vPos.x, vPos.y, false, GetDefSID());

  Vec3 vNorm;
  if(m_pVoxTerrain && GetCVars()->e_VoxTer)
    fZ = max(fZ, m_pVoxTerrain->GetElevation3D(vPos, vNorm));

  return fZ;
}

float C3DEngine::GetTerrainZ(int x, int y)
{
  Vec3 vNorm;
  if(m_pVoxTerrain)
    return m_pVoxTerrain->GetElevation3D(Vec3((float)x,(float)y,0.f), vNorm);

  if(x<0 || y<0 || x>=CTerrain::GetTerrainSize() || y>=CTerrain::GetTerrainSize())
    return TERRAIN_BOTTOM_LEVEL;
  return m_pTerrain ? m_pTerrain->GetZ(x, y, GetDefSID()) : 0;
}

bool C3DEngine::GetTerrainHole(int x, int y)
{
  return m_pTerrain ? m_pTerrain->GetHole(x, y, GetDefSID()) : false;
}

int C3DEngine::GetHeightMapUnitSize()
{
  return CTerrain::GetHeightMapUnitSize();
}

int C3DEngine::GetTerrainSize()
{
  return CTerrain::GetTerrainSize();
}

void C3DEngine::RemoveAllStaticObjects(int nSID)
{
  if(m_pTerrain)
    m_pTerrain->RemoveAllStaticObjects(nSID);
}

void C3DEngine::SetTerrainSurfaceType(int x, int y, int nType)
{
  assert(0);
  //  if(m_pTerrain)
  //  m_pTerrain->SetSurfaceType(x,y,nType);
}

void C3DEngine::SetTerrainSectorTexture( const int nTexSectorX, const int nTexSectorY, unsigned int textureId )
{
  if(m_pTerrain)
    m_pTerrain->SetTerrainSectorTexture(nTexSectorX, nTexSectorY, textureId, GetDefSID());
}

void C3DEngine::OnExplosion(Vec3 vPos, float fRadius, bool bDeformTerrain)
{
  if(GetCVars()->e_Decals == 2)
    PrintMessage("Debug: C3DEngine::OnExplosion: Pos=(%.1f,%.1f,%.1f) fRadius=%.2f", vPos.x, vPos.y, vPos.z, fRadius);

  if(vPos.x<0 || vPos.x>=CTerrain::GetTerrainSize() || vPos.y<0 || vPos.y>=CTerrain::GetTerrainSize() || fRadius<=0)
    return; // out of terrain

  if(m_pVoxTerrain && bDeformTerrain && GetCVars()->e_TerrainDeformations)
  {
    m_pVoxTerrain->DoVoxelShape(vPos, fRadius/2, -1, Vec3(255,255,255), eveoSubstract, evbsSphere, NULL, 0.5f);
    return;
  }

  // do not create decals near the terrain holes
  if(!m_pVoxTerrain)
  {
    for(int x=int(vPos.x-fRadius); x<=int(vPos.x+fRadius)+1; x+=CTerrain::GetHeightMapUnitSize())
      for(int y=int(vPos.y-fRadius); y<=int(vPos.y+fRadius)+1; y+=CTerrain::GetHeightMapUnitSize())
        if(m_pTerrain->GetHole(x,y, GetDefSID()))
          return;
  }

  // try to remove objects around
  bool bGroundDeformationAllowed = m_pTerrain->RemoveObjectsInArea(vPos,fRadius) && bDeformTerrain && GetCVars()->e_TerrainDeformations;

  // reduce ground decals size depending on distance to the ground
  float fExploHeight = vPos.z - GetTerrain()->GetZApr(vPos.x,vPos.y, false, GetDefSID());

  if(bGroundDeformationAllowed && (fExploHeight > -0.1f) && fExploHeight<fRadius && fRadius>0.125f)
  {
    m_pTerrain->MakeCrater(vPos,fRadius);

    // update roads
    {
      PodArray<IRenderNode*> lstRoads;
      AABB bbox(vPos-Vec3(fRadius,fRadius,fRadius), vPos+Vec3(fRadius,fRadius,fRadius));
      Get3DEngine()->GetObjectsByTypeGlobal(lstRoads, eERType_Road, &bbox);
      for(int i=0; i<lstRoads.Count(); i++)
      {
        CRoadRenderNode * pRoad = (CRoadRenderNode *)lstRoads[i];
        pRoad->OnTerrainChanged();
      }
    }
  }

  // delete decals what can not be correctly updated
  Vec3 vRadius(fRadius,fRadius,fRadius);
  AABB areaBox(vPos-vRadius, vPos+vRadius);
  Get3DEngine()->DeleteDecalsInRange( &areaBox, NULL );
}

float C3DEngine::GetMaxViewDistance( bool bScaled )
{
  // spec lerp factor
  float lerpSpec = clamp(GetCVars()->e_MaxViewDistSpecLerp, 0.0f, 1.0f);

  // camera height lerp factor
  float lerpHeight = clamp(max( 0.f, GetSystem()->GetViewCamera().GetPosition().z - GetWaterLevel()) / GetFloatCVar(e_MaxViewDistFullDistCamHeight), 0.0f, 1.0f);

  // lerp between specs
  float fMaxViewDist = m_fMaxViewDistLowSpec * (1.f - lerpSpec) + m_fMaxViewDistHighSpec * lerpSpec;

  // lerp between prev result and high spec
  fMaxViewDist = fMaxViewDist * (1.f - lerpHeight) + m_fMaxViewDistHighSpec * lerpHeight;

  if(bScaled)
    fMaxViewDist *= m_fMaxViewDistScale;

  // for debugging
  const float fMaxViewDistCVar = GetFloatCVar(e_MaxViewDistance);
  fMaxViewDist = (float)__fsel(fMaxViewDistCVar, fMaxViewDistCVar, fMaxViewDist);

  fMaxViewDist = (float)__fsel(fabsf(fMaxViewDist) - 0.100001f, fMaxViewDist, 0.100001f);

  return fMaxViewDist;
}

void C3DEngine::SetFogColor(const Vec3& vFogColor)
{
  m_vFogColor    = vFogColor;
  GetRenderer()->SetClearColor(m_vFogColor);
}

Vec3 C3DEngine::GetFogColor( )
{
  return m_vFogColor;
}

void C3DEngine::GetSkyLightParameters( Vec3& sunDir, Vec3& sunIntensity, float& Km, float& Kr, float& g, Vec3& rgbWaveLengths )
{
	CSkyLightManager::SSkyDomeCondition skyCond;
	m_pSkyLightManager->GetCurSkyDomeCondition( skyCond );

	g = skyCond.m_g;
	Km = skyCond.m_Km;
	Kr = skyCond.m_Kr;
	sunIntensity = skyCond.m_sunIntensity;
	rgbWaveLengths = skyCond.m_rgbWaveLengths;
	sunDir = skyCond.m_sunDirection;
}

void C3DEngine::SetSkyLightParameters( const Vec3& sunDir, const Vec3& sunIntensity, float Km, float Kr, float g, const Vec3& rgbWaveLengths, bool forceImmediateUpdate )
{
  CSkyLightManager::SSkyDomeCondition skyCond;

	skyCond.m_g = g;
	skyCond.m_Km = Km;
	skyCond.m_Kr = Kr;
	skyCond.m_sunIntensity = sunIntensity;
	skyCond.m_rgbWaveLengths = rgbWaveLengths;
	skyCond.m_sunDirection = sunDir;

  m_pSkyLightManager->SetSkyDomeCondition( skyCond );
  if (forceImmediateUpdate && IsHDRSkyMaterial(GetSkyMaterial()))
    m_pSkyLightManager->FullUpdate();
}

float C3DEngine::GetHDRDynamicMultiplier() const
{
  return m_fHDRDynamicMultiplier;
}

void C3DEngine::SetHDRDynamicMultiplier( const float value )
{
  m_fHDRDynamicMultiplier = clamp_tpl(value,0.01f,100.0f);
}

void C3DEngine::SetShadowMapRendering( int nLightFlags, int nShadowMapLod )
{
	if (nLightFlags & DLF_SUN)
	{
		assert(nShadowMapLod >= 0 && nShadowMapLod < 8);
		m_eShadowMapRendering = EShadowMapType(SHADOW_MAP_GSM0 + nShadowMapLod);
	}
	else if (nLightFlags & DLF_REFLECTIVE_SHADOWMAP)
		m_eShadowMapRendering = SHADOW_MAP_REFLECTIVE;
	else if ((nLightFlags & DLF_POINT) || (nLightFlags & DLF_PROJECT))
		m_eShadowMapRendering = SHADOW_MAP_LOCAL;
	else
		m_eShadowMapRendering = SHADOW_MAP_NONE;
}

bool C3DEngine::IsTessellationAllowedForShadowMap() const
{
#ifdef TESSELLATION_ENGINE
	switch (m_eShadowMapRendering)
	{
	case SHADOW_MAP_GSM0:
	case SHADOW_MAP_GSM1:
	case SHADOW_MAP_GSM2:
	case SHADOW_MAP_GSM3:
	case SHADOW_MAP_GSM4:
	case SHADOW_MAP_GSM5:
	case SHADOW_MAP_GSM6:
	case SHADOW_MAP_GSM7:
		return (m_eShadowMapRendering - SHADOW_MAP_GSM0) < GetCVars()->e_ShadowsTessellateCascades;
	case SHADOW_MAP_LOCAL:
		return GetCVars()->e_ShadowsTessellateDLights != 0;
	case SHADOW_MAP_NONE:
	case SHADOW_MAP_REFLECTIVE:
	case SHADOW_MAP_VOXEL:
	default:
		return false;
	}
#endif //#ifdef TESSELLATION_ENGINE

	return false;
}

bool C3DEngine::IsTessellationAllowed(const CRenderObject *pObj, bool bIgnoreShadowPass) const
{
#ifdef TESSELLATION_ENGINE
	assert(pObj && GetCVars());
	if (pObj->m_fDistance < GetCVars()->e_TessellationMaxDistance
		&& GetCVars()->e_Tessellation
		&& m_nRenderStackLevel == 0
		&& !(pObj->m_ObjFlags & FOB_DISSOLVE)) // dissolve is not working with tessellation for now
	{
		bool bAllowTessellation = true;

		// Check if rendering into shadow map and enable tessellation only if allowed
		if (!bIgnoreShadowPass && IsRenderingIntoShadowMap())
		{
			if (IsTessellationAllowedForShadowMap())
			{
				// NOTE: This might be useful for game projects
				// Use tessellation only for objects visible in main view
				// Shadows will switch to non-tessellated when caster gets out of view
				//IRenderNode *pRN = (IRenderNode*)pObj->m_pRenderNode;
				//if (pRN)
				//	bAllowTessellation = ( pRN->IsRenderNode() && (pRN->GetDrawFrame() > gRenDev->GetFrameID(true) - 10) );
			}
			else
				bAllowTessellation = false;
		}

		return bAllowTessellation;
	}
#endif //#ifdef TESSELLATION_ENGINE

	return false;
}

void C3DEngine::TraceFogVolumes( const Vec3& worldPos, ColorF& fogVolumeContrib )
{
  CFogVolumeRenderNode::TraceFogVolumes( worldPos, fogVolumeContrib );
}

void C3DEngine::SetPhysMaterialEnumerator(IPhysMaterialEnumerator * pPhysMaterialEnumerator)
{
  m_pPhysMaterialEnumerator = pPhysMaterialEnumerator;
}

IPhysMaterialEnumerator * C3DEngine::GetPhysMaterialEnumerator()
{
  return m_pPhysMaterialEnumerator;
}

void C3DEngine::ApplyForceToEnvironment(Vec3 vPos, float fRadius, float fAmountOfForce)
{
  if(m_pTerrain)
    m_pTerrain->ApplyForceToEnvironment(vPos, fRadius, fAmountOfForce);
}

float C3DEngine::GetDistanceToSectorWithWater()
{
  if(!m_pTerrain || !m_pTerrain->GetParentNode(0))
    return 100000.f;

  bool bCameraInTerrainBounds = Overlap::Point_AABB2D( GetCamera().GetPosition(), m_pTerrain->GetParentNode(0)->GetBBoxVirtual() );

  return (bCameraInTerrainBounds && (m_pTerrain && m_pTerrain->GetDistanceToSectorWithWater() > 0.1f))
    ? m_pTerrain->GetDistanceToSectorWithWater() : max(GetCamera().GetPosition().z - GetWaterLevel(),0.1f);
}


Vec3 C3DEngine::GetSkyColor() const
{
  return m_pObjManager ? m_pObjManager->m_vSkyColor : Vec3(0,0,0);
}

Vec3 C3DEngine::GetSunColor() const
{
  return m_pObjManager ? m_pObjManager->m_vSunColor : Vec3(0,0,0);
}

float C3DEngine::GetSkyBrightness() const
{
  return m_pObjManager ? m_pObjManager->m_fSkyBrightMul : 1.0f;
}

float C3DEngine::GetSSAOAmount() const
{
  return m_pObjManager ? m_pObjManager->m_fSSAOAmount: 1.0f;
}

float C3DEngine::GetSSAOContrast() const
{
  return m_pObjManager ? m_pObjManager->m_fSSAOContrast: 1.0f;
}

float C3DEngine::GetGIAmount() const
{
  return m_pObjManager ? m_pObjManager->m_fGIAmount: 1.0f;
}

float C3DEngine::GetSunRel() const
{
  return m_pObjManager->m_fSunSkyRel;
}

void C3DEngine::SetRainParams(const Vec3 & vCenter, float fRadius, float fAmount, const Vec3 & vColor)
{
  if (m_pObjManager)
  {
    m_pObjManager->m_rainParams.m_vWorldPos = vCenter;
    m_pObjManager->m_rainParams.m_fRadius = fRadius;
    m_pObjManager->m_rainParams.m_fAmount = fAmount;
    m_pObjManager->m_rainParams.m_vColor = vColor;
    m_pObjManager->m_rainParams.m_nValidFrameID = GetFrameID();
  }
}

void C3DEngine::SetRainParams(float fReflAmount, float fFakeGlossiness, float fPuddlesAmount, bool bRainDrops, float fRainDropsSpeed, float fUmbrellaRadius)
{
  if (m_pObjManager)
  {
    m_pObjManager->m_rainParams.m_fReflectionAmount = fReflAmount;
    m_pObjManager->m_rainParams.m_fFakeGlossiness = fFakeGlossiness;
    m_pObjManager->m_rainParams.m_fPuddlesAmount = fPuddlesAmount;
    m_pObjManager->m_rainParams.m_bRainDrops = bRainDrops;
    m_pObjManager->m_rainParams.m_fRainDropsSpeed = fRainDropsSpeed;
    m_pObjManager->m_rainParams.m_fUmbrellaRadius = fUmbrellaRadius;
  }
}

bool C3DEngine::GetRainParams(Vec3 & vCenter, float & fRadius, float & fAmount, Vec3 & vColor)
{
  bool bRet = false;
  const int nFrmID = GetFrameID();
  if (m_pObjManager)
  {
    vCenter = m_pObjManager->m_rainParams.m_vWorldPos;
    fRadius = m_pObjManager->m_rainParams.m_fRadius;
    fAmount = 0.f;
    if (IsOutdoorVisible())
      fAmount = m_pObjManager->m_rainParams.m_fAmount;
    vColor = m_pObjManager->m_rainParams.m_vColor;
    bRet = m_pObjManager->m_rainParams.m_nValidFrameID == nFrmID;
  }
  return bRet;
}

void C3DEngine::GetRainParams(float & fReflAmount, float & fFakeGlossiness, float & fPuddlesAmount, bool & bRainDrops, float & fRainDropsSpeed, float & fUmbrellaRadius) const
{
  if (m_pObjManager)
  {
    fReflAmount = m_pObjManager->m_rainParams.m_fReflectionAmount;
    fFakeGlossiness = m_pObjManager->m_rainParams.m_fFakeGlossiness;
    fPuddlesAmount = m_pObjManager->m_rainParams.m_fPuddlesAmount;
    bRainDrops = m_pObjManager->m_rainParams.m_bRainDrops;
    fRainDropsSpeed = m_pObjManager->m_rainParams.m_fRainDropsSpeed;
    fUmbrellaRadius = m_pObjManager->m_rainParams.m_fUmbrellaRadius;
  }
}

void C3DEngine::SetSnowSurfaceParams(const Vec3 & vCenter, float fRadius, float fSnowAmount, float fFrostAmount, float fSurfaceFreezing)
{
	if (m_pObjManager)
	{
		m_pObjManager->m_snowParams.m_vWorldPos = vCenter;
		m_pObjManager->m_snowParams.m_fRadius = fRadius;
		m_pObjManager->m_snowParams.m_fSnowAmount = fSnowAmount;
		m_pObjManager->m_snowParams.m_fFrostAmount = fFrostAmount;
		m_pObjManager->m_snowParams.m_fSurfaceFreezing = fSurfaceFreezing;
	}
}

bool C3DEngine::GetSnowSurfaceParams(Vec3 & vCenter, float & fRadius, float & fSnowAmount, float & fFrostAmount, float & fSurfaceFreezing )
{
	bool bRet = false;
	if (m_pObjManager)
	{
		vCenter = m_pObjManager->m_snowParams.m_vWorldPos;
		fRadius = m_pObjManager->m_snowParams.m_fRadius;
		fSnowAmount = 0.f;
		fFrostAmount = 0.f;
		fSurfaceFreezing = 0.f;
		if (IsOutdoorVisible())
		{
			fSnowAmount = m_pObjManager->m_snowParams.m_fSnowAmount;
			fFrostAmount = m_pObjManager->m_snowParams.m_fFrostAmount;
			fSurfaceFreezing = m_pObjManager->m_snowParams.m_fSurfaceFreezing;
		}
		bRet = true;
	}
	return bRet;
}

void C3DEngine::SetSnowFallParams(int nSnowFlakeCount, float fSnowFlakeSize, float fSnowFallBrightness, float fSnowFallGravityScale, float fSnowFallWindScale, float fSnowFallTurbulence, float fSnowFallTurbulenceFreq)
{
	if (m_pObjManager)
	{
		m_pObjManager->m_snowParams.m_nSnowFlakeCount = nSnowFlakeCount;
		m_pObjManager->m_snowParams.m_fSnowFlakeSize = fSnowFlakeSize;
		m_pObjManager->m_snowParams.m_fSnowFallBrightness = fSnowFallBrightness;
		m_pObjManager->m_snowParams.m_fSnowFallGravityScale = fSnowFallGravityScale;
		m_pObjManager->m_snowParams.m_fSnowFallWindScale = fSnowFallWindScale;
		m_pObjManager->m_snowParams.m_fSnowFallTurbulence = fSnowFallTurbulence;
		m_pObjManager->m_snowParams.m_fSnowFallTurbulenceFreq = fSnowFallTurbulenceFreq;
	}
}

bool C3DEngine::GetSnowFallParams(int & nSnowFlakeCount, float & fSnowFlakeSize, float & fSnowFallBrightness, float & fSnowFallGravityScale, float & fSnowFallWindScale, float & fSnowFallTurbulence, float & fSnowFallTurbulenceFreq )
{
	bool bRet = false;
	if (m_pObjManager)
	{
		nSnowFlakeCount = 0;
		fSnowFlakeSize = 0.f;
		fSnowFallBrightness = 0.f;
		fSnowFallGravityScale = 0.f;
		fSnowFallWindScale = 0.f;
		fSnowFallTurbulence = 0.f;
		fSnowFallTurbulenceFreq = 0.f;
		if (IsOutdoorVisible())
		{
			nSnowFlakeCount = m_pObjManager->m_snowParams.m_nSnowFlakeCount;
			fSnowFlakeSize = m_pObjManager->m_snowParams.m_fSnowFlakeSize;
			fSnowFallBrightness = m_pObjManager->m_snowParams.m_fSnowFallBrightness;
			fSnowFallGravityScale = m_pObjManager->m_snowParams.m_fSnowFallGravityScale;
			fSnowFallWindScale = m_pObjManager->m_snowParams.m_fSnowFallWindScale;
			fSnowFallTurbulence = m_pObjManager->m_snowParams.m_fSnowFallTurbulence;
			fSnowFallTurbulenceFreq = m_pObjManager->m_snowParams.m_fSnowFallTurbulenceFreq;
		}
		bRet = true;
	}
	return bRet;
}

float C3DEngine::GetTerrainTextureMultiplier(int nSID) const
{
  if(!m_pTerrain)
    return 1;

  return m_pTerrain->GetTerrainTextureMultiplier(nSID);
}

void C3DEngine::SetSunDir( const Vec3& newSunDir )
{ 
  Vec3 vSunDirNormalized = newSunDir.normalized();
  m_vSunDirRealtime = vSunDirNormalized;
  if( vSunDirNormalized.Dot(m_vSunDirNormalized) < GetFloatCVar(e_SunAngleSnapDot) || 
    GetCurTimeSec() - m_fSunDirUpdateTime > GetFloatCVar(e_SunAngleSnapSec) )
  {
    m_vSunDirNormalized = vSunDirNormalized;
    m_vSunDir = m_vSunDirNormalized * DISTANCE_TO_THE_SUN; 

    m_fSunDirUpdateTime = GetCurTimeSec();
  }
}

Vec3 C3DEngine::GetSunDir()  const
{ 
  return m_vSunDir; 
}

Vec3 C3DEngine::GetSunDirNormalized() const
{ 
  return m_vSunDirNormalized; 
}

Vec3 C3DEngine::GetRealtimeSunDirNormalized() const
{ 
  return m_vSunDirRealtime; 
}

Vec3 C3DEngine::GetAmbientColorFromPosition(const Vec3 & vPos, float fRadius) 
{
  Vec3 ret(0,0,0); 

  if(m_pObjManager)
  {
    if(CVisArea * pVisArea = (CVisArea *)m_pVisAreaManager->GetVisAreaFromPos(vPos))
      ret = pVisArea->GetFinalAmbientColor();
    else
      ret = Get3DEngine()->GetSkyColor();

    assert(ret.x>=0 && ret.y>=0 && ret.z>=0);
  }

  return ret;
}

void C3DEngine::FreeRenderNodeState(IRenderNode * pEnt)
{
  //  PrintMessage("vlad: C3DEngine::FreeRenderNodeState: %d, %s", (int)(IRenderNode*)pEnt, pEnt->GetName());

  /*
  LightMapInfo * pLightMapInfo = (LightMapInfo *)pEnt->GetLightMapInfo();
  if(pLightMapInfo && pLightMapInfo->pLMTCBuffer)
  GetRenderer()->ReleaseBuffer(pLightMapInfo->pLMTCBuffer);
  */
  // TODO: Delete data here, or at a more appropiate place 
  // (currently done in CBrush::~CBrush(), hope that is 'a more appropiate place')

#if !defined(_RELEASE)
  if(!gEnv->IsDedicated())
  {
    //As render nodes can be deleted in many places, it's possible that the map of render nodes used by stats gathering (r_stats 6, perfHUD, debug gun, Statoscope) could get aliased.
    //Ensure that this node is removed from the map to prevent a dereference after deletion.
    gEnv->pRenderer->ForceRemoveNodeFromDrawCallsMap( pEnt );
  }
#endif

  m_lstAlwaysVisible.Delete(pEnt);

	{
		// make sure object is not added to render list
		PodArray<SRenderMeshRenderInfo> &rList = GetObjManager()->m_arrSRenderMeshRenderList_RenderObject;
		for(int i=0; i<rList.Count(); i++)
		{
			if(rList[i].pEnt == pEnt)
			{
				Warning("C3DEngine::FreeRenderNodeState: Deleting of object when it is already added into rendering pipeline");
				GetObjManager()->m_arrSRenderMeshRenderList_RenderObjectData.Delete(i);
				rList.Delete(i);
				i--;
			}
		}
	}

  if(m_pDecalManager && (pEnt->m_nInternalFlags & IRenderNode::DECAL_OWNER))
    m_pDecalManager->OnEntityDeleted(pEnt);

  if(pEnt->GetRenderNodeType() == eERType_Light)
    GetRenderer()->OnEntityDeleted(pEnt);

  // Procedural vegetation in pure game seems the only case where we can rely on ERF_CASTSHADOWMAPS flag
  // and it is the only time critical one because of high amount of such objects
  // So only those objects are checked for ERF_CASTSHADOWMAPS flag before searching in casters list
  // TODO: use IRenderNode::WAS_CASTER flag when available
  if((pEnt->GetRndFlags() & ERF_CASTSHADOWMAPS) || !(pEnt->GetRndFlags() & ERF_PROCEDURAL))
  { // make sure pointer to object will not be used somewhere in the renderer
    Get3DEngine()->OnCasterDeleted(pEnt);
  }

  UnRegisterEntity(pEnt);

  if(pEnt->m_pRNTmpData)
  {
    Get3DEngine()->FreeRNTmpData(&pEnt->m_pRNTmpData);
    assert(!pEnt->m_pRNTmpData);
  }

  //#endif
  /*
  if(pEnt->m_pShadowMapInfo)
  {
  pEnt->m_pShadowMapInfo->Release(pEnt->GetRenderNodeType(), GetRenderer());
  pEnt->m_pShadowMapInfo = NULL;
  }
  */
  // check that was really unregistered
  /*  if(pEnt->m_pSector)
  for(int t=0; t<2; t++)
  {
  for(int e=0; e<pEnt->m_pSector->m_lstEntities[t].Count(); e++)
  {
  if(pEnt->m_pSector->m_lstEntities[t][e]->GetEntityRS() == pEntRendState)
  {
  assert(0); // UnRegisterInAllSectors do this already
  pEnt->m_pSector->m_lstEntities[t].Delete(e);
  }
  }

  pEnt->m_pSector->m_lstEntities[t].Delete(pEnt);
  }										 

  pEnt->m_pSector = 0;
  */

  // delete occlusion client
  /*	for(int i=0; i<2; i++)
  if(m_pOcclState && m_pOcclState->arrREOcclusionQuery[i])
  {
  m_pOcclState->arrREOcclusionQuery[i]->Release();
  m_pOcclState->arrREOcclusionQuery[i] = 0;
  }*/

  //  delete m_pOcclState;
  //m_pOcclState = 0;

  //todo: is it needed?
  //	pEnt->GetEntityVisArea() = 0;
}

const char * C3DEngine::GetLevelFilePath(const char * szFileName)
{
  strncpy_s(m_sGetLevelFilePathTmpBuff, m_szLevelFolder, MAX_PATH_LENGTH);
  if (*szFileName && (*szFileName == '/' || *szFileName == '\\'))
    strcat_s(m_sGetLevelFilePathTmpBuff, MAX_PATH_LENGTH, szFileName+1);
  else
    strcat_s(m_sGetLevelFilePathTmpBuff, MAX_PATH_LENGTH, szFileName);
  return m_sGetLevelFilePathTmpBuff;
}
/*
uint16 * C3DEngine::GetUnderWaterSmoothHMap(int & nDimensions)
{
return m_pTerrain ? m_pTerrain->GetUnderWaterSmoothHMap(nDimensions) : 0;
}

void C3DEngine::MakeUnderWaterSmoothHMap(int nWaterUnitSize)
{
if(m_pTerrain)
m_pTerrain->MakeUnderWaterSmoothHMap(nWaterUnitSize);
}
*/
void C3DEngine::SetTerrainBurnedOut(int x, int y, bool bBurnedOut)
{
  assert(!"not supported");
  /*  if(m_pTerrain)
  {
  m_pTerrain->SetBurnedOut(x, y, bBurnedOut);

  // update vert buffers
  CTerrainNode * pInfo = m_pTerrain->GetSecInfo(x,y);
  if(pInfo)
  pInfo->ReleaseHeightMapGeometry();
  }*/
}

bool C3DEngine::IsTerrainBurnedOut(int x, int y)
{
  assert(!"not supported");
  return 0;//m_pTerrain ? m_pTerrain->IsBurnedOut(x, y) : 0;
}

int C3DEngine::GetTerrainSectorSize()
{
  return m_pTerrain ? m_pTerrain->GetSectorSize() : 0;
}

void C3DEngine::ActivatePortal(const Vec3 &vPos, bool bActivate, const char * szEntityName)
{
  if (m_pVisAreaManager)
    m_pVisAreaManager->ActivatePortal(vPos, bActivate, szEntityName);
}

bool C3DEngine::SetStatInstGroup(int nGroupId, const IStatInstGroup & siGroup, int nSID)
{
  if(nSID < 0 || nSID >= m_pObjManager->m_lstStaticTypes.Count())
	  return false;

  m_fRefreshSceneDataCVarsSumm = -100;

  m_pObjManager->m_lstStaticTypes[nSID].resize(max(nGroupId+1, m_pObjManager->m_lstStaticTypes[nSID].Count()));

  if(nGroupId<0 || nGroupId>=m_pObjManager->m_lstStaticTypes[nSID].Count())
    return false;

  StatInstGroup & rGroup = m_pObjManager->m_lstStaticTypes[nSID][nGroupId];

  rGroup.pStatObj = siGroup.pStatObj;

  if(rGroup.pStatObj)
    strncpy(rGroup.szFileName, siGroup.pStatObj->GetFilePath(), sizeof(rGroup.szFileName));
  else
    rGroup.szFileName[0] = 0;

  rGroup.bHideability	= siGroup.bHideability;
  rGroup.bHideabilitySecondary	= siGroup.bHideabilitySecondary;
  rGroup.nPlayerHideable = siGroup.nPlayerHideable;
  rGroup.bPickable	  = siGroup.bPickable;
  rGroup.fBending			= siGroup.fBending;
  rGroup.bCastShadow   = siGroup.bCastShadow;
  rGroup.bRecvShadow   = siGroup.bRecvShadow;
  rGroup.bRecvDecals   = siGroup.bRecvDecals;
  rGroup.bPrecShadow   = siGroup.bPrecShadow;

  rGroup.bUseAlphaBlending						= siGroup.bUseAlphaBlending;
  rGroup.fSpriteDistRatio						= siGroup.fSpriteDistRatio;
  rGroup.fLodDistRatio						= siGroup.fLodDistRatio;
  rGroup.fShadowDistRatio						= siGroup.fShadowDistRatio;
  rGroup.fMaxViewDistRatio						= siGroup.fMaxViewDistRatio;

  rGroup.fBrightness									= siGroup.fBrightness;

  rGroup.pMaterial										= siGroup.pMaterial;	
  //	if(siGroup.pMaterial) // make sure mat id is set
  //	if(int nMaterialId = GetObjManager()->GetObjectMaterialId(rGroup.pMaterial))
  //	if(nMaterialId<0)
  //	rGroup.pMaterial = NULL;

  rGroup.bUseSprites						      = siGroup.bUseSprites;

  rGroup.fDensity										= siGroup.fDensity;
  rGroup.fElevationMax								= siGroup.fElevationMax;
  rGroup.fElevationMin								= siGroup.fElevationMin;
  rGroup.fSize												= siGroup.fSize;
  rGroup.fSizeVar										= siGroup.fSizeVar;
  rGroup.fSlopeMax										= siGroup.fSlopeMax;
  rGroup.fSlopeMin										= siGroup.fSlopeMin;

  rGroup.bRandomRotation             = siGroup.bRandomRotation;
  rGroup.nMaterialLayers = siGroup.nMaterialLayers;

  rGroup.bUseTerrainColor             = siGroup.bUseTerrainColor && Get3DEngine()->m_bShowTerrainSurface;
  rGroup.bAllowIndoor									= siGroup.bAllowIndoor;
  rGroup.bAlignToTerrain             = siGroup.bAlignToTerrain && Get3DEngine()->m_bShowTerrainSurface;
  rGroup.bAffectedByVoxels             = siGroup.bAffectedByVoxels;
  rGroup.minConfigSpec = siGroup.minConfigSpec;

  rGroup.Update(GetCVars(), Get3DEngine()->GetGeomDetailScreenRes());

  if (GetTerrain())
    GetTerrain()->MarkAllSectorsAsUncompiled(nSID);

  MarkRNTmpDataPoolForReset();

  return true;
}

bool C3DEngine::GetStatInstGroup(int nGroupId, IStatInstGroup & siGroup, int nSID)
{
  if(nSID < 0 || nSID >= m_pObjManager->m_lstStaticTypes.Count())
	  return false;

  if(nGroupId<0 || nGroupId>=m_pObjManager->m_lstStaticTypes[nSID].Count())
    return false;

  StatInstGroup & rGroup = m_pObjManager->m_lstStaticTypes[nSID][nGroupId];

  siGroup.pStatObj			= rGroup.pStatObj;
  if(siGroup.pStatObj)
    strncpy(siGroup.szFileName, rGroup.pStatObj->GetFilePath(), sizeof(siGroup.szFileName));

  siGroup.bHideability	= rGroup.bHideability;
  siGroup.bHideabilitySecondary	= rGroup.bHideabilitySecondary;
  siGroup.nPlayerHideable = rGroup.nPlayerHideable;
  siGroup.bPickable	    = rGroup.bPickable;
  siGroup.fBending			= rGroup.fBending;
  siGroup.bCastShadow   = rGroup.bCastShadow;
  siGroup.bRecvShadow   = rGroup.bRecvShadow;
  siGroup.bRecvDecals   = rGroup.bRecvDecals;
  siGroup.bPrecShadow   = rGroup.bPrecShadow;

  siGroup.bUseAlphaBlending						= rGroup.bUseAlphaBlending;
  siGroup.fSpriteDistRatio						= rGroup.fSpriteDistRatio;
  siGroup.fLodDistRatio               = rGroup.fLodDistRatio;
  siGroup.fShadowDistRatio						= rGroup.fShadowDistRatio;
  siGroup.fMaxViewDistRatio						= rGroup.fMaxViewDistRatio;

  siGroup.fBrightness									= rGroup.fBrightness;
  siGroup.pMaterial										= rGroup.pMaterial;
  siGroup.bUseSprites                 = rGroup.bUseSprites;

  siGroup.fDensity										= rGroup.fDensity;
  siGroup.fElevationMax								= rGroup.fElevationMax;
  siGroup.fElevationMin								= rGroup.fElevationMin;
  siGroup.fSize												= rGroup.fSize;
  siGroup.fSizeVar										= rGroup.fSizeVar;
  siGroup.fSlopeMax										= rGroup.fSlopeMax;
  siGroup.fSlopeMin										= rGroup.fSlopeMin;

  return true;
}

void C3DEngine::UpdateStatInstGroups()
{
  if(!m_pObjManager)
    return;

  for(uint32 nSID=0; nSID<m_pObjManager->m_lstStaticTypes.size(); nSID++)
  {
	  PodArray<StatInstGroup> &rGroupTable = m_pObjManager->m_lstStaticTypes[nSID];
	  for(uint32 nGroupId=0; nGroupId<rGroupTable.size(); nGroupId++)
	  {
		StatInstGroup & rGroup = rGroupTable[nGroupId];
		rGroup.Update(GetCVars(), Get3DEngine()->GetGeomDetailScreenRes());
	  }
  }
}

void C3DEngine::GetMemoryUsage(class ICrySizer * pSizer) const
{

#if !defined(_LIB) && !defined(__SPU__)// Only when compiling as dynamic library
  //	{
  //SIZER_COMPONENT_NAME(pSizer,"Strings");
  //pSizer->AddObject( (this+1),string::_usedMemory(0) );
  //	}
  {
    SIZER_COMPONENT_NAME(pSizer,"STL Allocator Waste");
    CryModuleMemoryInfo meminfo;
    ZeroStruct(meminfo);
    CryGetMemoryInfoForModule( &meminfo );
    pSizer->AddObject( (this+2),(size_t)meminfo.STL_wasted );
  }
#endif

  pSizer->AddObject(this, sizeof(*this) + (GetCVars()->e_StreamCgfDebug ? 100*1024*1024 : 0));
  pSizer->AddObject(m_pCVars);

  pSizer->AddObject(m_lstDynLights);
  pSizer->AddObject(m_lstDynLightsNoLight);
  pSizer->AddObject(m_lstStaticLights);
  pSizer->AddObject(m_lstAffectingLightsCombinations);
  pSizer->AddObject(m_arrLightProjFrustums);
  pSizer->AddObject(m_arrEntsInFoliage);

  pSizer->AddObject(m_lstVoxelObjectsForUpdate);
  pSizer->AddObject(m_lstRoadRenderNodesForUpdate);

  pSizer->AddObject(m_lstKilledVegetations);
  pSizer->AddObject(arrFPSforSaveLevelStats);
  pSizer->AddObject(m_lstAlwaysVisible);

  if (CTemporaryPool* pPool = CTemporaryPool::Get())
  {
    SIZER_COMPONENT_NAME(pSizer, "Temporary Pool");
    pPool->GetMemoryUsage(pSizer);
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "RenderMeshMerger");
    m_pRenderMeshMerger->GetMemoryUsage(pSizer);
  }

  CRoadRenderNode::GetMemoryUsageStatic( pSizer );

  {
    SIZER_COMPONENT_NAME(pSizer, "Particles");
    pSizer->AddObject(m_pPartManager);		
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "SkyLightManager");
    pSizer->AddObject( m_pSkyLightManager );
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "DecalManager");
    pSizer->AddObject(m_pDecalManager);		
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "OutdoorObjectsTree");
    pSizer->AddObject(m_pObjectsTree);	   
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "ObjManager");
    pSizer->AddObject(m_pObjManager);	  
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "Terrain");
    pSizer->AddObject(m_pTerrain);	 
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "VisAreas");
    pSizer->AddObject(m_pVisAreaManager);	
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "CoverageBuffer");
    pSizer->AddObject(m_pCoverageBuffer);	
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "RNTmpDataPool");

    CRNTmpData * pNext = NULL;

    for(CRNTmpData * pElem = m_LTPRootFree.pNext; pElem!=&m_LTPRootFree; pElem = pNext)
    {
      pSizer->AddObject(pElem, sizeof(*pElem));
      pNext = pElem->pNext;
    }

    for(CRNTmpData * pElem = m_LTPRootUsed.pNext; pElem!=&m_LTPRootUsed; pElem = pNext)
    {
      pSizer->AddObject(pElem, sizeof(*pElem));
      pNext = pElem->pNext;
    }


  }

  {
    SIZER_COMPONENT_NAME(pSizer, "ProcVegetPool");
    pSizer->AddObject(CTerrainNode::GetProcObjPoolMan());	
    pSizer->AddObject(CTerrainNode::GetProcObjChunkPool());	   
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "LayersBaseTextureData");
    pSizer->AddObject(m_arrBaseTextureData);    
  }

  {
    SIZER_COMPONENT_NAME(pSizer, "VoxTerrain");
    pSizer->AddObject(m_pVoxTerrain);    
  }

}

void C3DEngine::GetResourceMemoryUsage(ICrySizer * pSizer,const AABB& cstAABB)
{
  //////////////////////////////////////////////////////////////////////////
  std::vector<IRenderNode*>	cFoundRenderNodes;
  unsigned int							nFoundObjects(0);

  nFoundObjects=GetObjectsInBox(cstAABB);
  cFoundRenderNodes.resize(nFoundObjects,NULL);
  GetObjectsInBox(cstAABB,&cFoundRenderNodes.front());

  size_t nCurrentRenderNode(0);
  size_t nTotalNumberOfRenderNodes(0);

  nTotalNumberOfRenderNodes=cFoundRenderNodes.size();
  for (nCurrentRenderNode=0;nCurrentRenderNode<nTotalNumberOfRenderNodes;++nCurrentRenderNode)
  {
    IRenderNode*& piRenderNode=cFoundRenderNodes[nCurrentRenderNode];

    IMaterial*	piMaterial(piRenderNode->GetMaterialOverride());
    if (!piMaterial)
    {
      piMaterial=piRenderNode->GetMaterial();			
    }

    if (piMaterial)
    {
      piMaterial->GetResourceMemoryUsage(pSizer);
    }

    {
      IRenderMesh*	piMesh(NULL);
      size_t				nCount(0);

      piMesh=piRenderNode->GetRenderMesh(nCount);
      for (;piMesh;++nCount,piMesh=piRenderNode->GetRenderMesh(nCount))
      {
        // Timur, RenderMesh may not be loaded due to streaming!
        piMesh->GetMemoryUsage(pSizer,IRenderMesh::MEM_USAGE_COMBINED);
      }				
    }
  }
  //////////////////////////////////////////////////////////////////////////

  if (m_pTerrain)
  {
    CTerrainNode	*poTerrainNode=m_pTerrain->FindMinNodeContainingBox(cstAABB, GetDefSID());
    if (poTerrainNode)
    {
      poTerrainNode->GetResourceMemoryUsage(pSizer,cstAABB);
    }
  }


  //{
  //	SIZER_COMPONENT_NAME(pSizer, "Particles");
  //	if(m_pPartManager)
  //		m_pPartManager->GetMemoryUsage(pSizer);
  //}

  //pSizer->AddObject(m_lstDynLights);
  //pSizer->AddObject(m_lstDynLightsNoLight);
  //pSizer->AddObject(m_lstStaticLights);
  //pSizer->AddObject(m_lstAffectingLightsCombinations);
  //pSizer->AddObject(m_arrLightProjFrustums);
  //pSizer->AddObject(m_arrEntsInFoliage);

  //pSizer->AddObject(m_lstVoxelObjectsForUpdate);
  //pSizer->AddObject(m_lstRoadRenderNodesForUpdate);

  //pSizer->AddObject(m_lstKilledVegetations);
  //pSizer->AddObject(arrFPSforSaveLevelStats);
  //pSizer->AddObject(m_lstAlwaysVisible);
  //pSizer->AddObject(m_pCVars, sizeof(CVars));

  //if(m_pDecalManager)
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "DecalManager");
  //	m_pDecalManager->GetMemoryUsage(pSizer);
  //	pSizer->AddObject(m_pDecalManager, sizeof(*m_pDecalManager));
  //}

  //if(m_pObjectsTree[nSID])
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "OutdoorObjectsTree");
  //	m_pObjectsTree[nSID]->GetMemoryUsage(pSizer);
  //}

  //if(m_pObjManager)
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "ObjManager");
  //	m_pObjManager->GetMemoryUsage(pSizer);
  //}

  //if (m_pTerrain)
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "Terrain");
  //	m_pTerrain->GetMemoryUsage(pSizer);
  //}

  //if (m_pVisAreaManager)
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "VisAreas");
  //	m_pVisAreaManager->GetMemoryUsage(pSizer);
  //}

  //if(m_pCoverageBuffer)
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "ObjManager");
  //	pSizer->AddObject(m_pCoverageBuffer, sizeof(*m_pCoverageBuffer));
  //}

  //if(m_pCoverageBuffer)
  //	m_pCoverageBuffer->GetMemoryUsage(pSizer);

  //{
  //	SIZER_COMPONENT_NAME(pSizer, "RNTmpDataPool");

  //	CRNTmpData * pNext = NULL;

  //	for(CRNTmpData * pElem = m_LTPRootFree.pNext; pElem!=&m_LTPRootFree; pElem = pNext)
  //	{
  //		pSizer->AddObject(pElem, sizeof(*pElem));
  //		pNext = pElem->pNext;
  //	}

  //	for(CRNTmpData * pElem = m_LTPRootUsed.pNext; pElem!=&m_LTPRootUsed; pElem = pNext)
  //	{
  //		pSizer->AddObject(pElem, sizeof(*pElem));
  //		pNext = pElem->pNext;
  //	}


  //	//    pSizer->AddObject(&m_RNTmpDataPools, m_RNTmpDataPools.GetMemUsage());
  //}

  //if(CProcVegetPoolMan * pMan = CTerrainNode::GetProcObjPoolMan())
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "ProcVegetPool");

  //	if (pMan)
  //		pMan->GetMemoryUsage(pSizer);

  //	SProcObjChunkPool * pPool = CTerrainNode::GetProcObjChunkPool();
  //	if (pPool)
  //		pPool->GetMemoryUsage(pSizer);
  //}

  //if(m_arrBaseTextureData.Count())
  //{
  //	SIZER_COMPONENT_NAME(pSizer, "LayersBaseTextureData");

  //	for(int i=0; i<m_arrBaseTextureData.Count(); i++)
  //		pSizer->AddObject(&m_arrBaseTextureData[i], m_arrBaseTextureData[i].GetMemoryUsage());
  //}

  //if(m_pVoxTerrain)
  //	m_pVoxTerrain->GetMemoryUsage(pSizer);
}

bool C3DEngine::IsUnderWater( const Vec3& vPos ) const
{
  bool bUnderWater = false;
  for (IPhysicalEntity* pArea = 0; pArea = GetPhysicalWorld()->GetNextArea(pArea); )
  {
    if (bUnderWater)
      // Must finish iteration to unlock areas.
      continue;

    pe_params_buoyancy buoy;
    if (pArea->GetParams(&buoy) && buoy.iMedium == 0)
    {
      if (!is_unused(buoy.waterPlane.n))
      {
        if (buoy.waterPlane.n * vPos < buoy.waterPlane.n * buoy.waterPlane.origin)
        {
          pe_status_contains_point st;
          st.pt = vPos;
          if (pArea->GetStatus(&st))
            bUnderWater = true;
        }
      }
    }
  }
  return bUnderWater;
}

void C3DEngine::SetOceanRenderFlags( uint8 nFlags )
{
  m_nOceanRenderFlags = nFlags;
}

uint32 C3DEngine::GetOceanVisiblePixelsCount() const
{
  return COcean::GetVisiblePixelsCount();
}

float C3DEngine::GetBottomLevel(const Vec3 & referencePos, float maxRelevantDepth, int objflags)
{
  FUNCTION_PROFILER_3DENGINE;

  const float terrainWorldZ = GetTerrainElevation(referencePos.x, referencePos.y);

  const float padding = 0.2f;
  float rayLength;

  // NOTE: Terrain is above referencePos, so referencePos is probably inside a voxel or something.
  if (terrainWorldZ <= referencePos.z)
    rayLength = min(maxRelevantDepth, (referencePos.z - terrainWorldZ));
  else
    rayLength = maxRelevantDepth;

  rayLength += padding * 2.0f;

  ray_hit hit;
  int rayFlags = geom_colltype_player<<rwi_colltype_bit | rwi_stop_at_pierceable;
  if (m_pPhysicalWorld->RayWorldIntersection(referencePos + Vec3(0,0,padding), Vec3(0,0,-rayLength), 
    ent_terrain|ent_static|ent_sleeping_rigid|ent_rigid, rayFlags, &hit, 1))
  {
    return hit.pt.z;
  }

  // Terrain was above or too far below referencePos, and no solid object was close enough.
  return BOTTOM_LEVEL_UNKNOWN;
}

float C3DEngine::GetBottomLevel(const Vec3 & referencePos, float maxRelevantDepth /* = 10.0f */)
{
  return GetBottomLevel (referencePos, maxRelevantDepth, ent_terrain|ent_static|ent_sleeping_rigid|ent_rigid);
}

float C3DEngine::GetBottomLevel(const Vec3 & referencePos, int objflags)
{
  return GetBottomLevel (referencePos, 10.0f, objflags);
}

float C3DEngine::GetWaterLevel(const Vec3 * pvPos, Vec3 * pvFlowDir, bool bAccurate)
{
  FUNCTION_PROFILER_3DENGINE;

  bool bInVisarea = m_pVisAreaManager && m_pVisAreaManager->GetVisAreaFromPos(*pvPos)!=0;

  Vec3 gravity;
  pe_params_buoyancy pb[4];
  int i,nBuoys=m_pPhysicalWorld->CheckAreas(*pvPos,gravity,pb,4,0);

  float max_level = (!bInVisarea)? (bAccurate? GetAccurateOceanHeight( *pvPos ): GetWaterLevel()) : WATER_LEVEL_UNKNOWN;

  for(i=0;i<nBuoys;i++)
  { 
    if (pb[i].iMedium==0 && (!bInVisarea || fabs_tpl(pb[i].waterPlane.origin.x)+fabs_tpl(pb[i].waterPlane.origin.y)>0))
    {
      float fVolumeLevel = pvPos->z-pb[i].waterPlane.n.z*(pb[i].waterPlane.n*(*pvPos-pb[i].waterPlane.origin));

      // it's not ocean
      if( pb[i].waterPlane.origin.y + pb[i].waterPlane.origin.x != 0.0f)
        max_level = max(max_level, fVolumeLevel);
    }
  }

  return max(WATER_LEVEL_UNKNOWN, max_level);
}

float C3DEngine::GetWaterLevel()
{
  return m_pTerrain? m_pTerrain->CTerrain::GetWaterLevel() : WATER_LEVEL_UNKNOWN;
}

void C3DEngine::SetShadowsGSMCache(bool bCache)
{	 
  if (bCache)
    m_nGsmCache = GetCVars()->e_GsmCache;
  else
    m_nGsmCache = 0;
}

float C3DEngine::GetAccurateOceanHeight( const Vec3 &pCurrPos ) const
{
  FUNCTION_PROFILER_3DENGINE;

  float fWaterLevel = m_pTerrain? m_pTerrain->CTerrain::GetWaterLevel() : 0.f;
  fWaterLevel += COcean::GetWave( pCurrPos, Cry3DEngineBase::GetFrameID() );
  return fWaterLevel;
}

Vec4 C3DEngine::GetCausticsParams() const
{
  return Vec4(m_oceanCausticsTilling, m_oceanCausticsDistanceAtten, m_oceanCausticsMultiplier, 1.0);
}

Vec4 C3DEngine::GetOceanAnimationCausticsParams() const
{
  return Vec4(1,m_oceanCausticHeight,m_oceanCausticDepth,m_oceanCausticIntensity);
}

void C3DEngine::GetHDRSetupParams(Vec4 pParams[7]) const
{
	pParams[0] = Vec4( 1.0f, m_hdrBloomMul, m_hdrFlaresMul, m_hdrStreaksMul);
	pParams[1] = Vec4( m_hdrBlueShift, m_hdrStreakScale);
	pParams[2] = Vec4( m_hdrBloomColor, m_fGrainAmount);
	pParams[3] = Vec4( m_hdrFlaresColor * m_hdrFlaresMul, 1.0f);
	pParams[4] = Vec4( m_hdrStreaksColor * m_hdrStreaksMul, m_fHDRContrast);
  pParams[5] = Vec4( m_fHDRShoulderScale, m_fHDRMidtonesScale, m_fHDRToeScale, m_fHDRWhitePoint);
  pParams[6] = Vec4( m_vColorBalance.x, m_vColorBalance.y, m_vColorBalance.z, m_fHDRSaturation);
};

void C3DEngine::GetOceanAnimationParams(Vec4 &pParams0, Vec4 &pParams1 ) const
{
  static int nFrameID = -1;
  int nCurrFrameID = Cry3DEngineBase::GetFrameID();

  static Vec4 s_pParams0 = Vec4(0,0,0,0);
  static Vec4 s_pParams1 = Vec4(0,0,0,0);

  if( nFrameID != nCurrFrameID && m_pTerrain)
  {
    cry_sincosf(m_oceanWindDirection, &s_pParams1.y, &s_pParams1.z);

    s_pParams0 = Vec4(m_oceanWindDirection,  m_oceanWindSpeed, m_oceanWavesSpeed, m_oceanWavesAmount);
    s_pParams1.x = m_oceanWavesSize;
    s_pParams1.w = m_pTerrain->GetWaterLevel();

    nFrameID = nCurrFrameID;
  }

  pParams0 = s_pParams0;
  pParams1 = s_pParams1;
};

IVisArea * C3DEngine::CreateVisArea(uint64 visGUID)
{
  return m_pObjManager ? m_pVisAreaManager->CreateVisArea(visGUID) : 0;
}

void C3DEngine::DeleteVisArea(IVisArea * pVisArea)
{
  if(!m_pVisAreaManager->IsValidVisAreaPointer((CVisArea*)pVisArea))
  {
    Warning( "I3DEngine::DeleteVisArea: Invalid VisArea pointer");
    return;
  }
  if(m_pObjManager)
  {
    CVisArea * pArea = (CVisArea*)pVisArea;

    //		if(pArea->m_pObjectsTree)
    //		pArea->m_pObjectsTree->FreeAreaBrushes(true);

    PodArray<SRNInfo> lstEntitiesInArea;
    if(pArea->m_pObjectsTree)
      pArea->m_pObjectsTree->MoveObjectsIntoList(&lstEntitiesInArea,NULL);

    // unregister from indoor
    for(int i=0; i<lstEntitiesInArea.Count(); i++)
      Get3DEngine()->UnRegisterEntity(lstEntitiesInArea[i].pNode);

    if(pArea->m_pObjectsTree)
      assert(pArea->m_pObjectsTree->GetObjectsCount(eMain)==0);

    m_pVisAreaManager->DeleteVisArea((CVisArea*)pVisArea);

    for(int i=0; i<lstEntitiesInArea.Count(); i++)
      Get3DEngine()->RegisterEntity(lstEntitiesInArea[i].pNode);
  }
}

void C3DEngine::UpdateVisArea(IVisArea * pVisArea, const Vec3 * pPoints, int nCount, const char * szName, 
                              const SVisAreaInfo & info, bool bReregisterObjects)
{
  if(!m_pObjManager)
    return;

  CVisArea * pArea = (CVisArea*)pVisArea;

  //	PrintMessage("C3DEngine::UpdateVisArea: %s", szName);

  Vec3 vTotalBoxMin = pArea->m_boxArea.min;
  Vec3 vTotalBoxMax = pArea->m_boxArea.max;

  m_pVisAreaManager->UpdateVisArea((CVisArea*)pVisArea, pPoints, nCount, szName, info);

  if(((CVisArea*)pVisArea)->m_pObjectsTree && ((CVisArea*)pVisArea)->m_pObjectsTree->GetObjectsCount(eMain))
  {
    // merge old and new bboxes
    vTotalBoxMin.CheckMin(pArea->m_boxArea.min);
    vTotalBoxMax.CheckMax(pArea->m_boxArea.max);
  }
  else
  {
    vTotalBoxMin = pArea->m_boxArea.min;
    vTotalBoxMax = pArea->m_boxArea.max;
  }

  if(bReregisterObjects)
    m_pObjManager->ReregisterEntitiesInArea(vTotalBoxMin - Vec3(8,8,8), vTotalBoxMax + Vec3(8,8,8));
}

void C3DEngine::ResetParticlesAndDecals()
{
  if(m_pPartManager)
		m_pPartManager->Reset(true);

  if(m_pDecalManager)
    m_pDecalManager->Reset();

  CStatObjFoliage *pFoliage,*pFoliageNext;
  for(pFoliage=m_pFirstFoliage; &pFoliage->m_next!=&m_pFirstFoliage; pFoliage=pFoliageNext) {
    pFoliageNext = pFoliage->m_next;
    delete pFoliage;
  }

  ReRegisterKilledVegetationInstances();
}

IRenderNode * C3DEngine::CreateRenderNode( EERType type )
{
  switch (type)
  {
  case eERType_Brush:
    {
      CBrush * pBrush = new CBrush();
      return pBrush;
    }
  case eERType_Vegetation:
    {
      CVegetation * pVeget = new CVegetation();
      return pVeget;
    }
  case eERType_Cloud:
    {
      CCloudRenderNode* pCloud = new CCloudRenderNode();
      return pCloud;
    }
  case eERType_VoxelObject:
    {
      CVoxelObject* pVoxArea = new CVoxelObject(Vec3(0,0,0), DEF_VOX_UNIT_SIZE, 
        DEF_VOX_VOLUME_SIZE, DEF_VOX_VOLUME_SIZE, DEF_VOX_VOLUME_SIZE);
      return pVoxArea;
    }
  case eERType_FogVolume:
    {
      CFogVolumeRenderNode* pFogVolume = new CFogVolumeRenderNode();
      return pFogVolume;
    }
  case eERType_Road:
    {
      CRoadRenderNode* pRoad = new CRoadRenderNode();
      return pRoad;
    }
  case eERType_Decal:
    {
      CDecalRenderNode* pDecal = new CDecalRenderNode();
      return pDecal;
    }
  case eERType_WaterVolume:
    {
      CWaterVolumeRenderNode* pWaterVolume = new CWaterVolumeRenderNode();
      return pWaterVolume;
    }
  case eERType_WaterWave:
    {
      CWaterWaveRenderNode *pRenderNode = new CWaterWaveRenderNode();
      return pRenderNode;
    }
  case eERType_DistanceCloud:
    {
      CDistanceCloudRenderNode *pRenderNode = new CDistanceCloudRenderNode();
      return pRenderNode;
    }
  case eERType_AutoCubeMap:
    {
      CAutoCubeMapRenderNode *pRenderNode = new CAutoCubeMapRenderNode();
      return pRenderNode;
    }
  case eERType_Rope:
    {
      IRopeRenderNode *pRenderNode = new CRopeRenderNode();
      return pRenderNode;
    }
  case eERType_VolumeObject:
    {
      IVolumeObjectRenderNode* pRenderNode = new CVolumeObjectRenderNode();
      return pRenderNode;
    }
	case eERType_LightPropagationVolume:
		{
			ILPVRenderNode* pRenderNode = new CLPVRenderNode();
      return pRenderNode;
    }

#if !defined(EXCLUDE_DOCUMENTATION_PURPOSE)
  case eERType_PrismObject:
    {
      IPrismRenderNode* pRenderNode = new CPrismRenderNode();
      return pRenderNode;
    }
#endif // EXCLUDE_DOCUMENTATION_PURPOSE

	case eERType_BreakableGlass:
		{
			IBreakableGlassRenderNode* pRenderNode = new CBreakableGlassRenderNode();
			return pRenderNode;
		}

  case eERType_LightShape:
    {
      ILightShapeRenderNode* pRenderNode = new CLightShapeRenderNode();
      return pRenderNode;
    }

  }
  assert( !"C3DEngine::CreateRenderNode: Specified node type is not supported." );
  return 0;
}

void C3DEngine::DeleteRenderNode(IRenderNode * pRenderNode)
{
  UnRegisterEntity(pRenderNode);
  //	m_pObjManager->m_lstBrushContainer.Delete((CBrush*)pRenderNode);
  //m_pObjManager->m_lstVegetContainer.Delete((CVegetation*)pRenderNode);
  delete pRenderNode;
}

void C3DEngine::SetWind( const Vec3 & vWind )
{
  m_vWindSpeed = vWind;
  if (!m_vWindSpeed.IsZero())
  {
    // Maintain a large physics area for global wind.
    if (!m_pGlobalWind)
    {
      primitives::sphere geomSphere;
      geomSphere.center = Vec3(0);
      geomSphere.r = 1e7f;
      IGeometry *pGeom = m_pPhysicalWorld->GetGeomManager()->CreatePrimitive( primitives::sphere::type, &geomSphere );
      m_pGlobalWind = GetPhysicalWorld()->AddArea( pGeom, Vec3(ZERO), Quat(IDENTITY), 1.0f );

      pe_params_foreign_data fd;
      fd.iForeignFlags = PFF_OUTDOOR_AREA;
      m_pGlobalWind->SetParams(&fd);
    }

		// Set medium half-plane above water level. Set standard air density.
    pe_params_buoyancy pb;
    pb.iMedium = 1;
    pb.waterPlane.n.Set(0,0,-1);
    pb.waterPlane.origin.Set(0,0,GetWaterLevel());
    pb.waterFlow = m_vWindSpeed;
    pb.waterResistance = pb.waterDensity = 1;
    m_pGlobalWind->SetParams(&pb);
  }
  else if (m_pGlobalWind)
  {
    GetPhysicalWorld()->DestroyPhysicalEntity( m_pGlobalWind );
    m_pGlobalWind = 0;
  }
}

Vec3 C3DEngine::GetWind( const AABB& box, bool bIndoors ) const
{
  FUNCTION_PROFILER_3DENGINE;

  if (!m_pCVars->e_Wind)
    return Vec3(ZERO);

  // Start with global wind.
  Vec3 vWind = GetGlobalWind(bIndoors);

  if (m_pCVars->e_WindAreas)
  {
    // Iterate all areas, looking for wind areas. Skip first (global) area, and global wind.
    pe_params_buoyancy pb;

    IPhysicalEntity* pArea = GetPhysicalWorld()->GetNextArea(0);
    if (pArea)
    {
      assert(!(pArea->GetParams(&pb) && pb.iMedium == 1));
      while (pArea = GetPhysicalWorld()->GetNextArea(pArea))
      {
        if (pArea != m_pGlobalWind && pArea->GetParams(&pb) && pb.iMedium == 1)
        {
          if (bIndoors)
          {
            // Skip outdoor areas.
            pe_params_foreign_data fd;
            if (pArea->GetParams(&fd) && (fd.iForeignFlags & PFF_OUTDOOR_AREA))
              continue;
          }
          pe_status_area area;
          area.ctr = box.GetCenter();				
          area.size = box.GetSize() * 0.5f;          
          if (pArea->GetStatus(&area))
            vWind += area.pb.waterFlow;
        }
      }
    }
  }
  return vWind;
}

Vec3 C3DEngine::GetGlobalWind( bool bIndoors ) const
{
	FUNCTION_PROFILER_3DENGINE;

	// We assume indoor wind is zero.
	return (!m_pCVars->e_Wind || bIndoors) ? Vec3Constants<f32>::fVec3_Zero : m_vWindSpeed;
}

IVisArea * C3DEngine::GetVisAreaFromPos(const Vec3 &vPos )
{
  if(m_pObjManager && m_pVisAreaManager)
    return m_pVisAreaManager->GetVisAreaFromPos(vPos);

  return 0;
}

bool C3DEngine::IntersectsVisAreas(const AABB& box, void** pNodeCache)
{
  if(m_pObjManager && m_pVisAreaManager)
    return m_pVisAreaManager->IntersectsVisAreas(box, pNodeCache);
  return false;
}

bool C3DEngine::ClipToVisAreas(IVisArea* pInside, Sphere& sphere, Vec3 const& vNormal, void* pNodeCache)
{
  if (pInside)
    return pInside->ClipToVisArea(true, sphere, vNormal);
  else if (m_pVisAreaManager)
    return m_pVisAreaManager->ClipOutsideVisAreas(sphere, vNormal, pNodeCache);
  return false;
}

bool C3DEngine::IsVisAreasConnected(IVisArea * pArea1, IVisArea * pArea2, int nMaxRecursion, bool bSkipDisabledPortals)
{
  if (pArea1==pArea2)
    return (true);	// includes the case when both pArea1 & pArea2 are NULL (totally outside)
  // not considered by the other checks
  if (!pArea1 || !pArea2)
    return (false); // avoid a crash - better to put this check only
  // here in one place than in all the places where this function is called

  nMaxRecursion *= 2; // include portals since portals are the areas

  if(m_pObjManager && m_pVisAreaManager)
    return ((CVisArea*)pArea1)->FindVisArea((CVisArea*)pArea2, nMaxRecursion, bSkipDisabledPortals);

  return false;
}

bool C3DEngine::IsOutdoorVisible()
{
  if(m_pObjManager && m_pVisAreaManager)
    return m_pVisAreaManager->IsOutdoorAreasVisible();

  return false;
}

void C3DEngine::EnableOceanRendering(bool bOcean)
{
  m_bOcean = bOcean;
}

//////////////////////////////////////////////////////////////////////////
IMaterialManager* C3DEngine::GetMaterialManager()
{
  return m_pMatMan;
}
//////////////////////////////////////////////////////////////////////////

bool C3DEngine::IsTerrainHightMapModifiedByGame()
{
  return m_pTerrain ? (m_pTerrain->m_bHeightMapModified!=0) : 0;
}

void C3DEngine::CheckPhysicalized(const Vec3 & vBoxMin, const Vec3 & vBoxMax)
{
  /*  if(!GetCVars()->e_stream_areas)
  return;

  CVisArea * pVisArea = (CVisArea *)GetVisAreaFromPos((vBoxMin+vBoxMax)*0.5f);
  if(pVisArea)
  { // indoor
  pVisArea->CheckPhysicalized();
  IVisArea * arrConnections[16];
  int nConections = pVisArea->GetRealConnections(arrConnections,16);
  for(int i=0; i<nConections; i++)
  ((CVisArea*)arrConnections[i])->CheckPhysicalized();
  }
  else
  { // outdoor
  CTerrainNode * arrSecInfo[] = 
  {
  m_pTerrain->GetSecInfo(int(vBoxMin.x), int(vBoxMin.y)),
  m_pTerrain->GetSecInfo(int(vBoxMax.x), int(vBoxMin.y)),
  m_pTerrain->GetSecInfo(int(vBoxMin.x), int(vBoxMax.y)),
  m_pTerrain->GetSecInfo(int(vBoxMax.x), int(vBoxMax.y))
  };

  for(int i=0; i<4; i++)
  if(arrSecInfo[i])
  arrSecInfo[i]->CheckPhysicalized();
  }*/
}

void C3DEngine::AddUpdatedArea(AABB const& bb)
{
	// Merge with existing bb if close enough
	AUTO_LOCK(m_PhysAreaChangeLock);
	static const float fMERGE_THRESHOLD = 2.f;
	float fNewVolume = bb.GetVolume();
	for (uint i = 0; i < m_listPhysAreasChanged.size(); i++)
	{
		AABB bbUnion = bb;
		bbUnion.Add(m_listPhysAreasChanged[i]);
		if (bbUnion.GetVolume() <= (fNewVolume + m_listPhysAreasChanged[i].GetVolume()) * fMERGE_THRESHOLD)
		{
			m_listPhysAreasChanged[i] = bbUnion;
			return;
		}
	}
	m_listPhysAreasChanged.push_back(bb);
}

void C3DEngine::UpdatePhysAreasChanged()
{
	AUTO_LOCK(m_PhysAreaChangeLock);
	if (m_listPhysAreasChanged.empty())
		return;

	PodArray<IRenderNode*> lstObjects;
	for (uint i = 0; i < m_listPhysAreasChanged.size(); i++)
	{
		CopyObjects(&m_listPhysAreasChanged[i], &lstObjects);
	}
	m_listPhysAreasChanged.resize(0);

	for (uint i = 0; i < lstObjects.size(); i++)
	{
		IRenderNode* pNode = lstObjects[i];
		// Flag wind as invalid
		if (pNode->m_pRNTmpData)
			pNode->m_pRNTmpData->userData.bWindCurrent = 0;
		pNode->OnPhysAreaChange();
	}
}

void C3DEngine::CheckMemoryHeap()
{
  assert (IsHeapValid());
}

bool C3DEngine::SetMaterialFloat( char * szMatName, int nSubMatId, int nTexSlot, char * szParamName, float fValue )
{
  IMaterial * pMat = GetMaterialManager()->FindMaterial( szMatName );
  if(!pMat)
  { Warning("I3DEngine::SetMaterialFloat: Material not found: %s", szMatName); return false; }

  if(nSubMatId>0)
  {
    if(nSubMatId<=pMat->GetSubMtlCount())
      pMat = pMat->GetSubMtl(nSubMatId-1);
    else
    { 
      Warning("I3DEngine::SetMaterialFloat: SubMaterial not found: %s, SubMatId: %d", szMatName, nSubMatId); 
      return false; 
    }
  }

  SEfResTexture * pTex = pMat->GetShaderItem().m_pShaderResources->GetTexture(nTexSlot);
  if(!pTex)
  { Warning("I3DEngine::SetMaterialFloat: Texture slot not found: %s, TexSlot: %d", szMatName, nTexSlot); return false; }

  char szM_Name[256] = "";

  if(strncmp(szParamName,"m_", 2))
    strcpy_s(szM_Name,"m_");

  strcat_s(szM_Name,szParamName);

  SEfTexModificator& modif = *pTex->AddModificator();
  const bool res = modif.SetMember( szM_Name, fValue );
  return res;
}

void C3DEngine::CloseTerrainTextureFile(int nSID)
{
  if(m_pTerrain)
    m_pTerrain->CloseTerrainTextureFile(nSID);
}

//////////////////////////////////////////////////////////////////////////
int C3DEngine::GetLoadedObjectCount()
{
  int nObjectsLoaded = m_pObjManager ? m_pObjManager->GetLoadedObjectCount() : 0;

  if(gEnv->pCharacterManager)
  {
    ICharacterManager::Statistics AnimStats;
    gEnv->pCharacterManager->GetStatistics(AnimStats);
    nObjectsLoaded += AnimStats.numAnimObjectModels + AnimStats.numCharModels;
  }

  nObjectsLoaded += GetInstCount(eERType_VoxelObject);

  return nObjectsLoaded;
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetLoadedStatObjArray( IStatObj** pObjectsArray,int &nCount )
{
  if (m_pObjManager)
    m_pObjManager->GetLoadedStatObjArray( pObjectsArray,nCount );
  else
    nCount = 0;
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetObjectsStreamingStatus(SObjectsStreamingStatus &outStatus)
{
  if (m_pObjManager)
  {
    m_pObjManager->GetObjectsStreamingStatus(outStatus);
  }
  else
  {
    memset(&outStatus, 0, sizeof(outStatus));
  }
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetStatisticsData(SStatisticData &outData)
{
  gEnv->pRenderer->GetTextureRequests(&outData.textureRequests);
  gEnv->pRenderer->GetTextureRenders(&outData.textureRenders);
  gEnv->pRenderer->GetTexturePoolUsage(&outData.texturePoolMemUsage);
  gEnv->pRenderer->GetTexturePoolWanted(&outData.texturePoolMemRequired);
  gEnv->pRenderer->GetTextureUpdates(&outData.textureUpdates);
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetStreamingSubsystemData(EStreamTaskType subsystem, SStremaingBandwidthData &outData)
{
  switch (subsystem)
  {
  case eStreamTaskTypeSound: gEnv->pSoundSystem->GetIAudioDevice()->GetBandwidthStats(&outData.fBandwidthRequested);
    break;
  case eStreamTaskTypeGeometry: m_pObjManager->GetBandwidthStats(&outData.fBandwidthRequested);
    break;
  case eStreamTaskTypeTexture: gEnv->pRenderer->GetBandwidthStats(&outData.fBandwidthRequested);
    break;
  }
  gEnv->pSystem->GetStreamEngine()->GetBandwidthStats(subsystem,&outData.fBandwidthActual);
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::DeleteEntityDecals(IRenderNode * pEntity)
{
  if(m_pDecalManager && pEntity && (pEntity->m_nInternalFlags & IRenderNode::DECAL_OWNER))
    m_pDecalManager->OnEntityDeleted(pEntity);
}

void C3DEngine::CompleteObjectsGeometry()
{
  while(m_pTerrain && m_pTerrain->Voxel_Recompile_Modified_Incrementaly_Objects());
  while(m_pTerrain && m_pTerrain->Recompile_Modified_Incrementaly_RoadRenderNodes());

  CRoadRenderNode::FreeStaticMemoryUsage();
}

void C3DEngine::DeleteDecalsInRange( AABB * pAreaBox, IRenderNode * pEntity )
{
  if(m_pDecalManager)
    m_pDecalManager->DeleteDecalsInRange( pAreaBox, pEntity );
}

void C3DEngine::LockCGFResources()
{
  if(m_pObjManager)
    m_pObjManager->m_bLockCGFResources = 1;
}

void C3DEngine::UnlockCGFResources()
{
  if(m_pObjManager)
  {
    bool bNeedToFreeCGFs = m_pObjManager->m_bLockCGFResources != 0;
    m_pObjManager->m_bLockCGFResources = 0;
    if (bNeedToFreeCGFs)
    {
      m_pObjManager->FreeNotUsedCGFs();
    }
  }
}

void CLightEntity::ShadowMapInfo::Release(struct IRenderer * pRenderer)
{
  /*	if(pShadowMapCasters)
  {
  //		pShadowMapCasters->Clear();

  //		delete pShadowMapCasters;
  pShadowMapCasters = 0;
  }
  */
  /*	if(	pShadowMapFrustumContainer )//&& eEntType != eERType_Vegetation ) // vegetations share same frustum allocated in CStatObj
  {
  delete pShadowMapFrustumContainer->m_LightFrustums.Get(0)->pEntityList;
  pShadowMapFrustumContainer->m_LightFrustums.Get(0)->pEntityList=0;

  delete pShadowMapFrustumContainer;
  pShadowMapFrustumContainer=0;
  }

  if(	pShadowMapFrustumContainerPassiveCasters )//&& eEntType != eERType_Vegetation ) // vegetations share same frustum allocated in CStatObj
  {
  delete pShadowMapFrustumContainerPassiveCasters->m_LightFrustums.Get(0)->pEntityList;
  pShadowMapFrustumContainerPassiveCasters->m_LightFrustums.Get(0)->pCastersList=0;

  delete pShadowMapFrustumContainerPassiveCasters;
  pShadowMapFrustumContainerPassiveCasters=0;
  }*/
  /*
  if(pShadowMapRenderMeshsList)
  {
  for(int i=0; i<pShadowMapRenderMeshsList->Count(); i++)
  {
  if(pShadowMapRenderMeshsList->GetAt(i))
  {
  pRenderer->DeleteRenderMesh(pShadowMapRenderMeshsList->GetAt(i));
  pShadowMapRenderMeshsList->GetAt(i)=0;
  }
  }
  delete pShadowMapRenderMeshsList;
  pShadowMapRenderMeshsList=0;
  }*/

  delete this;
}

Vec3 C3DEngine::GetTerrainSurfaceNormal(Vec3 vPos) 
{ 
  if(m_pVoxTerrain && !GetCVars()->e_VoxTerHeightmapEditing)
    return m_pVoxTerrain->GetTerrainSurfaceNormal3D(vPos, .1f);

  return m_pTerrain ? m_pTerrain->GetTerrainSurfaceNormal(vPos, 0.5f*GetHeightMapUnitSize(), false) : Vec3(0.f,0.f,1.f); 
}

IMemoryBlock * C3DEngine::Voxel_GetObjects(Vec3 vPos, float fRadius, int nSurfaceTypeId, EVoxelEditOperation eOperation, EVoxelBrushShape eShape, EVoxelEditTarget eTarget)
{
  PodArray<CVoxelObject*> lstVoxAreas;

  Vec3 vBaseColor(0,0,0);

  if(m_pTerrain)
    m_pTerrain->DoVoxelShape(vPos, fRadius, nSurfaceTypeId, vBaseColor, eOperation, eShape, eTarget, &lstVoxAreas);

  if(m_pVisAreaManager)
    m_pVisAreaManager->DoVoxelShape(vPos, fRadius, nSurfaceTypeId, vBaseColor, eOperation, eShape, eTarget, &lstVoxAreas);

  CMemoryBlock * pMemBlock = new CMemoryBlock();
  pMemBlock->SetData(lstVoxAreas.GetElements(),lstVoxAreas.GetDataSize());

  return pMemBlock;
}

void C3DEngine::Voxel_Paint(Vec3 vPos, float fRadius, int nSurfaceTypeId, Vec3 vBaseColor, EVoxelEditOperation eOperation, EVoxelBrushShape eShape, EVoxelEditTarget eTarget, PodArray<IRenderNode*> * pBrushes, float fVoxelSize)
{
  if(eOperation==eveoCreateSoft || eOperation==eveoSubstractSoft || eOperation==eveoCreate || eOperation==eveoSubstract || eOperation==eveoMaterial || eOperation==eveoBaseColor)
  {
    if(nSurfaceTypeId<0)
    {
      Warning("C3DEngine::Voxel_Paint: Surface type is not specified");
      return;
    }
  }

  if(GetCVars()->e_VoxTer)
  {
    if(!m_pVoxTerrain)
    {
      SVoxTerrainInfo info;
      const float terrainSize = (float)GetTerrainSize();
      info.aabbTerrain = AABB(Vec3(0,0,0), Vec3(terrainSize, terrainSize, terrainSize));
      m_pVoxTerrain = new CVoxTerrain(info);
    }

    return m_pVoxTerrain->DoVoxelShape(vPos, fRadius, nSurfaceTypeId, vBaseColor*255.f, eOperation, eShape, pBrushes, fVoxelSize);
  }

  if(m_pTerrain)
    m_pTerrain->DoVoxelShape(vPos, fRadius, nSurfaceTypeId, vBaseColor, eOperation, eShape, eTarget, NULL);

  if(m_pVisAreaManager)
    m_pVisAreaManager->DoVoxelShape(vPos, fRadius, nSurfaceTypeId, vBaseColor, eOperation, eShape, eTarget, NULL);

  CrySleep(GetCVars()->e_VoxTerPaintSleep); // limit update frequency
}

//////////////////////////////////////////////////////////////////////////
IIndexedMesh* C3DEngine::CreateIndexedMesh()
{
  return new CIndexedMesh();
}

void C3DEngine::Voxel_SetFlags(bool bPhysics, bool bSimplify, bool bShadows, bool bMaterials)
{
  if(m_pTerrain)
    m_pTerrain->Voxel_SetFlags(bPhysics, bSimplify, bShadows, bMaterials);
}


void C3DEngine::SerializeState( TSerialize ser )
{
  if (ser.IsReading())
  {
    CStatObjFoliage *pFoliage,*pFoliageNext;
    for(pFoliage=m_pFirstFoliage; &pFoliage->m_next!=&m_pFirstFoliage; pFoliage=pFoliageNext) {
      pFoliageNext = pFoliage->m_next;
      delete pFoliage;
    }
  }

  ser.Value("m_sunRotationZ", m_sunRotationZ);
  ser.Value("m_sunRotationLongitude", m_sunRotationLongitude);

	ser.Value("m_bOcean", m_bOcean);
	ser.Value("m_moonRotationLatitude", m_moonRotationLatitude);
	ser.Value("m_moonRotationLongitude", m_moonRotationLongitude);
	ser.Value("m_eShadowMode", *(alias_cast<int*>(&m_eShadowMode)) );
	if (ser.IsReading())
		UpdateMoonDirection();

  if(m_pTerrain)
    m_pTerrain->SerializeTerrainState(ser);

  if(m_pDecalManager)
    m_pDecalManager->Serialize(ser);

  m_pPartManager->Serialize(ser);
  m_pTimeOfDay->Serialize(ser);
}

void C3DEngine::PostSerialize( bool bReading )
{
  m_pPartManager->PostSerialize(bReading);
}

void C3DEngine::InitMaterialDefautMappingAxis(IMaterial * pMat)
{
  uint8 arrProj[3] = {'X', 'Y', 'Z'};
  pMat->m_ucDefautMappingAxis = 'Z';
  pMat->m_fDefautMappingScale = 1.f;
  for(int c=0; c<3 && c<pMat->GetSubMtlCount(); c++)
  {
    IMaterial * pSubMat = (IMaterial*)pMat->GetSubMtl(c);
    pSubMat->m_ucDefautMappingAxis = arrProj[c];
    pSubMat->m_fDefautMappingScale = pMat->m_fDefautMappingScale;
  }
}

//////////////////////////////////////////////////////////////////////////
CContentCGF* C3DEngine::CreateChunkfileContent( const char *filename )
{
  return new CContentCGF(filename);
}
void C3DEngine::ReleaseChunkfileContent( CContentCGF* pCGF)
{
  delete pCGF;
}

bool C3DEngine::LoadChunkFileContent( CContentCGF* pCGF, const char *filename,bool bNoWarningMode )
{
  class Listener : public ILoaderCGFListener
  {
  public:
    virtual ~Listener(){}
    virtual void Warning( const char *format ) {Cry3DEngineBase::Warning("%s", format);}
    virtual void Error( const char *format ) {Cry3DEngineBase::Error("%s", format);}
    virtual bool IsValidationEnabled( ) { return Cry3DEngineBase::GetCVars()->e_StatObjValidate != 0; }
  };
  Listener listener;

  if( !pCGF )
  {
    FileWarning( 0,filename,"CGF Loading Failed: no content instance passed");
  }
  else
  {
    CLoaderCGF cgf;

#if !defined(XENON) && !defined(PS3)
    //CReadOnlyChunkFile *pChunkFile = new CReadOnlyChunkFile(true,bNoWarningMode);


#endif
    CReadOnlyChunkFile *pChunkFile = new CReadOnlyChunkFile(true,bNoWarningMode);

    if (cgf.LoadCGF( pCGF, filename, *pChunkFile, &listener ))
    {
      pCGF->SetChunkFile(pChunkFile);
      return true; 
    }
    else delete pChunkFile;
  }

  return false;
}

//////////////////////////////////////////////////////////////////////////
IChunkFile * C3DEngine::CreateChunkFile( bool bReadOnly )
{
  if (bReadOnly)
    return new CReadOnlyChunkFile(false);
  else
    return new CChunkFile;
}

int C3DEngine::GetTerrainTextureNodeSizeMeters()
{
  if(m_pTerrain)
    return m_pTerrain->GetTerrainTextureNodeSizeMeters();
  return 0;
}

int C3DEngine::GetTerrainTextureNodeSizePixels(int nLayer)
{
  if(m_pTerrain)
    return m_pTerrain->GetTerrainTextureNodeSizePixels(nLayer,0);
  return 0;
}

void C3DEngine::SetHeightMapMaxHeight(float fMaxHeight)
{
  if(m_pTerrain)
    m_pTerrain->SetHeightMapMaxHeight(fMaxHeight);
}

ITerrain * C3DEngine::CreateTerrain(const STerrainInfo & TerrainInfo)
{
  delete m_pTerrain;
  m_pTerrain = new CTerrain(TerrainInfo);
  return (ITerrain *)m_pTerrain;
}

void C3DEngine::DeleteTerrain()
{
  delete m_pTerrain;
  m_pTerrain = NULL;
}

IVoxTerrain * C3DEngine::CreateVoxTerrain(const SVoxTerrainInfo & Info)
{
  SAFE_DELETE(m_pVoxTerrain);
  if(GetCVars()->e_VoxTer)
    m_pVoxTerrain = new CVoxTerrain(Info);
  return (IVoxTerrain *)m_pVoxTerrain;
}

void C3DEngine::DeleteVoxTerrain()
{
  SAFE_DELETE(m_pVoxTerrain);
}

//void C3DEngine::SetStreamableListener(IStreamedObjectListener* pListener)
//{
//	m_pStreamListener = pListener;
//}

/*
void C3DEngine::RenderImposterContent(class CREImposter * pImposter, const CCamera & cam)
{
if(m_pTerrain && pImposter)
{
// set camera and disable frustum culling
CCamera oldCam = Get3DEngine()->GetCamera();
Get3DEngine()->SetCamera(cam);
GetCVars()->e_camera_frustum_test = false;

// start rendering
GetRenderer()->EF_StartEf();  

// prepare lights
UpdateLightSources();
PrepareLightSourcesForRendering();    

// render required terrain sector
m_pTerrain->RenderImposterContent(pImposter, cam);

// setup fog
SetupDistanceFog();

// finish rendering
GetRenderer()->EF_EndEf3D(SHDF_SORT | SHDF_ZPASS);

// restore
Get3DEngine()->SetCamera(oldCam);
GetCVars()->e_camera_frustum_test = true;
}
}*/

void C3DEngine::GetVoxelRenderNodes(struct IRenderNode**pRenderNodes, int & nCount)
{
}

void C3DEngine::PrecacheLevel(bool bPrecacheAllVisAreas, Vec3 * pPrecachePoints, int nPrecachePointsNum)
{
  LOADING_TIME_PROFILE_SECTION;

  if(GetVisAreaManager())
    GetVisAreaManager()->PrecacheLevel(bPrecacheAllVisAreas, pPrecachePoints, nPrecachePointsNum);
}

//////////////////////////////////////////////////////////////////////////
ITimeOfDay* C3DEngine::GetTimeOfDay()
{
  CTimeOfDay* tod = m_pTimeOfDay;

  if (!tod)
  {
    tod = new CTimeOfDay;
    m_pTimeOfDay = tod;
  }

  return tod;
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetGlobalParameter( E3DEngineParameter param,const Vec3 &v )
{
  float fValue = v.x;
  switch (param)
  {
  case E3DPARAM_SKY_COLOR:
    SetSkyColor( v );
    break;
  case E3DPARAM_SUN_COLOR:
    SetSunColor( v );
    break;

  case E3DPARAM_HDR_BLUE_SHIFT:
		m_hdrBlueShift = v;
		break;
	case E3DPARAM_HDR_BLUE_SHIFT_THRESHOLD:
		m_hdrBlueShiftThreshold = v.x;
    break;

  case E3DPARAM_AMBIENT_GROUND_COLOR:
    m_vAmbGroundCol = v;
    break;
  case E3DPARAM_AMBIENT_MIN_HEIGHT:
    m_fAmbMaxHeight = v.x;
    break;
  case E3DPARAM_AMBIENT_MAX_HEIGHT:
    m_fAmbMinHeight = v.x;
    break;

	case E3DPARAM_SKY_HIGHLIGHT_POS:
		m_vSkyHightlightPos = v;
		break;
	case E3DPARAM_SKY_HIGHLIGHT_COLOR:
		m_vSkyHightlightCol = v;
		break;
	case E3DPARAM_SKY_HIGHLIGHT_SIZE:
		m_fSkyHighlightSize = fValue > 0.0f ? fValue : 0.0f;
		break;
	case E3DPARAM_VOLFOG_RAMP:
		m_volFogRamp = v;
		break;
	case E3DPARAM_VOLFOG_SHADOW_RANGE:
		m_volFogShadowRange = v;
		break;
	case E3DPARAM_VOLFOG_SHADOW_DARKENING:
		m_volFogShadowDarkening = v;
		break;
	case E3DPARAM_VOLFOG_SHADOW_ENABLE:
		m_volFogShadowEnable = v;
		break;
	case E3DPARAM_NIGHSKY_HORIZON_COLOR:
		m_nightSkyHorizonCol = v;
		break;
	case E3DPARAM_NIGHSKY_ZENITH_COLOR:
		m_nightSkyZenithCol = v;
		break;
	case E3DPARAM_NIGHSKY_ZENITH_SHIFT:
		m_nightSkyZenithColShift = v.x;
		break;
	case E3DPARAM_NIGHSKY_STAR_INTENSITY:
		m_nightSkyStarIntensity = v.x;
		break;
	case E3DPARAM_NIGHSKY_MOON_COLOR:
		m_nightMoonCol = v;
		break;
	case E3DPARAM_NIGHSKY_MOON_SIZE:
		m_nightMoonSize = v.x;
		break;
	case E3DPARAM_NIGHSKY_MOON_INNERCORONA_COLOR:
		m_nightMoonInnerCoronaCol = v;
		break;
	case E3DPARAM_NIGHSKY_MOON_INNERCORONA_SCALE:
		m_nightMoonInnerCoronaScale = v.x;
		break;
	case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_COLOR:
		m_nightMoonOuterCoronaCol = v;
		break;
	case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_SCALE:
		m_nightMoonOuterCoronaScale = v.x;
		break;
	case E3DPARAM_OCEANFOG_COLOR_MULTIPLIER:
		m_oceanFogColorMultiplier = v.x;
		break;
	case E3DPARAM_OCEANFOG_COLOR:
		m_oceanFogColor = v * m_oceanFogColorMultiplier;
		break;
	case E3DPARAM_OCEANFOG_DENSITY:
		m_oceanFogDensity = v.x;
		break;
	case E3DPARAM_SKY_MOONROTATION:
		m_moonRotationLatitude = v.x;
		m_moonRotationLongitude = v.y;
		UpdateMoonDirection();
		break;
	case E3DPARAM_SKYBOX_MULTIPLIER:
		m_skyboxMultiplier = v.x;
		break;
	case E3DPARAM_DAY_NIGHT_INDICATOR:
		m_dayNightIndicator = v.x;
		gEnv->pSoundSystem->SetGlobalParameter("daylight", m_dayNightIndicator);
		break;
	case E3DPARAM_FOG_COLOR2:
		m_fogColor2 = v;
		break;
	case E3DPARAM_FOG_RADIAL_COLOR:
		m_fogColorRadial = v;
		break;
	case E3DPARAM_VOLFOG_HEIGHT_DENSITY:
		m_volFogHeightDensity = Vec3(v.x, v.y, 0);
		break;
	case E3DPARAM_VOLFOG_HEIGHT_DENSITY2:
		m_volFogHeightDensity2 = Vec3(v.x, v.y, 0);
		break;
	case E3DPARAM_VOLFOG_GRADIENT_CTRL:
		m_volFogGradientCtrl = v;
		break;
	case E3DPARAM_VOLFOG_GLOBAL_DENSITY:
		m_volFogGlobalDensity = v.x;
		m_volFogFinalDensityClamp = v.z;
		break;
  case E3DPARAM_COLORGRADING_COLOR_SATURATION:
    m_fSaturation = fValue;
    SetPostEffectParam("ColorGrading_Saturation", m_fSaturation);
    break;
  case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_COLOR:
    m_pPhotoFilterColor = Vec4(v.x, v.y, v.z, 1);
    SetPostEffectParamVec4("clr_ColorGrading_PhotoFilterColor", m_pPhotoFilterColor);
    break;
  case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_DENSITY:
    m_fPhotoFilterColorDensity = fValue;
    SetPostEffectParam("ColorGrading_PhotoFilterColorDensity", m_fPhotoFilterColorDensity);
    break;
  case E3DPARAM_COLORGRADING_FILTERS_GRAIN:
    m_fGrainAmount = fValue;
    SetPostEffectParam("ColorGrading_GrainAmount", m_fGrainAmount);
    break;
	case E3DPARAM_SKY_SKYBOX_ANGLE: // sky box rotation
		m_fSkyBoxAngle = fValue;
		break;
	case E3DPARAM_SKY_SKYBOX_STRETCHING: // sky box stretching
		m_fSkyBoxStretching = fValue;
		break;
	case E3DPARAM_SKY_SUNROTATION:
		m_sunRotationZ = v.x;
		m_sunRotationLongitude = v.y;
		break;
  case E3DPARAM_HDR_FILMCURVE_SHOULDER_SCALE:
    m_fHDRShoulderScale = v.x;
    break;
  case E3DPARAM_HDR_FILMCURVE_LINEAR_SCALE:
    m_fHDRMidtonesScale = v.x;
    break;
  case E3DPARAM_HDR_FILMCURVE_TOE_SCALE:
    m_fHDRToeScale = v.x;
    break;
  case E3DPARAM_HDR_FILMCURVE_WHITEPOINT:
    m_fHDRWhitePoint = v.x;
    break;
  case E3DPARAM_HDR_COLORGRADING_COLOR_SATURATION:
    m_fHDRSaturation = v.x;
    break;
  case E3DPARAM_HDR_COLORGRADING_COLOR_CONTRAST:
    m_fHDRContrast = v.x;
    break;
  case E3DPARAM_HDR_COLORGRADING_COLOR_BALANCE:
    m_vColorBalance = v;
    break;
	case E3DPARAM_CLOUDSHADING_MULTIPLIERS:
		m_fCloudShadingSunLightMultiplier = (v.x >= 0) ? v.x : 0;
		m_fCloudShadingSkyLightMultiplier = (v.y >= 0) ? v.y : 0;
		break;
	case E3DPARAM_CLOUDSHADING_SUNCOLOR:
		m_vCloudShadingCustomSunColor = v;
		break;
	case E3DPARAM_CLOUDSHADING_SKYCOLOR:
		m_vCloudShadingCustomSkyColor = v;
		break;
	case E3DPARAM_NIGHSKY_MOON_DIRECTION: // moon direction is fixed per level or updated via FG node (E3DPARAM_SKY_MOONROTATION)
	default:
		assert(0);
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::GetGlobalParameter( E3DEngineParameter param,Vec3 &v )
{
  switch (param)
  {
  case E3DPARAM_SKY_COLOR:
    v = GetSkyColor();
    break;
  case E3DPARAM_SUN_COLOR:
    v = GetSunColor();
    break;

  case E3DPARAM_HDR_BLUE_SHIFT:
		v = m_hdrBlueShift;
		break;
	case E3DPARAM_HDR_BLUE_SHIFT_THRESHOLD:
		v.x = m_hdrBlueShiftThreshold;
    break;

	case E3DPARAM_AMBIENT_GROUND_COLOR:
		v = m_vAmbGroundCol;
		break;
	case E3DPARAM_AMBIENT_MIN_HEIGHT:
		v.x = m_fAmbMaxHeight;
		break;
	case E3DPARAM_AMBIENT_MAX_HEIGHT:
		v.x = m_fAmbMinHeight;
		break;		
	case E3DPARAM_AMBIENT_AFFECT_GLOBALCUBEMAP:
		v = Vec3(m_bHeightBasedAmbientAffectsGlobalCube? 1.0f : 0.0f, 0, 0);
		break;
	case E3DPARAM_SKY_HIGHLIGHT_POS:
		v = m_vSkyHightlightPos;
		break;
	case E3DPARAM_SKY_HIGHLIGHT_COLOR:
		v = m_vSkyHightlightCol;
		break;
	case E3DPARAM_SKY_HIGHLIGHT_SIZE:
		v.x = m_fSkyHighlightSize;
		break;
	case E3DPARAM_VOLFOG_RAMP:
		v = m_volFogRamp;
		break;
	case E3DPARAM_VOLFOG_SHADOW_RANGE:
		v = m_volFogShadowRange;
		break;
	case E3DPARAM_VOLFOG_SHADOW_DARKENING:
		v = m_volFogShadowDarkening;
		break;
	case E3DPARAM_VOLFOG_SHADOW_ENABLE:
		v = m_volFogShadowEnable;
		break;
	case E3DPARAM_NIGHSKY_HORIZON_COLOR:
		v = m_nightSkyHorizonCol;
		break;
	case E3DPARAM_NIGHSKY_ZENITH_COLOR:
		v = m_nightSkyZenithCol;
		break;
	case E3DPARAM_NIGHSKY_ZENITH_SHIFT:
		v = Vec3( m_nightSkyZenithColShift, 0, 0 );
		break;
	case E3DPARAM_NIGHSKY_STAR_INTENSITY:
		v = Vec3( m_nightSkyStarIntensity, 0, 0 );
		break;
	case E3DPARAM_NIGHSKY_MOON_DIRECTION:
		v = m_moonDirection;
		break;
	case E3DPARAM_NIGHSKY_MOON_COLOR:
		v = m_nightMoonCol;
		break;
	case E3DPARAM_NIGHSKY_MOON_SIZE:
		v = Vec3( m_nightMoonSize, 0, 0 );
		break;
	case E3DPARAM_NIGHSKY_MOON_INNERCORONA_COLOR:
		v = m_nightMoonInnerCoronaCol;
		break;
	case E3DPARAM_NIGHSKY_MOON_INNERCORONA_SCALE:
		v = Vec3( m_nightMoonInnerCoronaScale, 0, 0 );
		break;
	case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_COLOR:
		v = m_nightMoonOuterCoronaCol;
		break;
	case E3DPARAM_NIGHSKY_MOON_OUTERCORONA_SCALE:
		v = Vec3( m_nightMoonOuterCoronaScale, 0, 0 );
		break;
	case E3DPARAM_SKY_SUNROTATION:
		v = Vec3(m_sunRotationZ, m_sunRotationLongitude, 0);
		break;
	case E3DPARAM_SKY_MOONROTATION:
		v = Vec3(m_moonRotationLatitude, m_moonRotationLongitude, 0);
		break;
	case E3DPARAM_OCEANFOG_COLOR_MULTIPLIER:
		v = Vec3(m_oceanFogColorMultiplier, 0, 0);
		break;
	case E3DPARAM_OCEANFOG_COLOR:
		v = m_oceanFogColor;
		break;
	case E3DPARAM_OCEANFOG_DENSITY:
		v = Vec3(m_oceanFogDensity, 0, 0);
		break;
	case E3DPARAM_SKYBOX_MULTIPLIER:
		v = Vec3(m_skyboxMultiplier, 0, 0);
		break;
	case E3DPARAM_DAY_NIGHT_INDICATOR:
		v = Vec3(m_dayNightIndicator, 0, 0);
		break;
	case E3DPARAM_FOG_COLOR2:
		v = m_fogColor2;
		break;
	case E3DPARAM_FOG_RADIAL_COLOR:
		v = m_fogColorRadial;
		break;
	case E3DPARAM_VOLFOG_HEIGHT_DENSITY:
		v = m_volFogHeightDensity;
		break;
	case E3DPARAM_VOLFOG_HEIGHT_DENSITY2:
		v = m_volFogHeightDensity2;
		break;
	case E3DPARAM_VOLFOG_GRADIENT_CTRL:
		v = m_volFogGradientCtrl;
		break;
	case E3DPARAM_VOLFOG_GLOBAL_DENSITY:
		v = Vec3(m_volFogGlobalDensity, m_volFogGlobalDensityMultiplierLDR, m_volFogFinalDensityClamp);
		break;
  case E3DPARAM_COLORGRADING_COLOR_SATURATION:
    v = Vec3(m_fSaturation, 0, 0);
    break;
  case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_COLOR:
    v = Vec3(m_pPhotoFilterColor.x, m_pPhotoFilterColor.y, m_pPhotoFilterColor.z);
    break;
  case E3DPARAM_COLORGRADING_FILTERS_PHOTOFILTER_DENSITY:
    v = Vec3(m_fPhotoFilterColorDensity, 0, 0);
    break;
  case E3DPARAM_COLORGRADING_FILTERS_GRAIN:
    v = Vec3(m_fGrainAmount, 0, 0);
  case E3DPARAM_HDR_FILMCURVE_SHOULDER_SCALE:
    v = Vec3(m_fHDRShoulderScale, 0, 0);
    break;
  case E3DPARAM_HDR_FILMCURVE_LINEAR_SCALE:
    v = Vec3(m_fHDRMidtonesScale, 0, 0);
    break;
  case E3DPARAM_HDR_FILMCURVE_TOE_SCALE:
    v = Vec3(m_fHDRToeScale, 0, 0);
    break;
  case E3DPARAM_HDR_FILMCURVE_WHITEPOINT:
    v = Vec3(m_fHDRWhitePoint, 0, 0);
    break;
  case E3DPARAM_HDR_COLORGRADING_COLOR_SATURATION:
    v = Vec3(m_fHDRSaturation, 0, 0);
    break;
  case E3DPARAM_HDR_COLORGRADING_COLOR_CONTRAST:
    v = Vec3(m_fHDRContrast, 0, 0);
    break;
  case E3DPARAM_HDR_COLORGRADING_COLOR_BALANCE:
    v = m_vColorBalance;
    break;
	case E3DPARAM_CLOUDSHADING_MULTIPLIERS:
		v = Vec3(m_fCloudShadingSunLightMultiplier, m_fCloudShadingSkyLightMultiplier, 0);
		break;
	case E3DPARAM_CLOUDSHADING_SUNCOLOR:
		v = m_vCloudShadingCustomSunColor;
		break;
	case E3DPARAM_CLOUDSHADING_SKYCOLOR:
		v = m_vCloudShadingCustomSkyColor;
		break;
	default:
		assert(0);
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void C3DEngine::SetShadowsCascadesBias(const float *pCascadeConstBias, const float *pCascadeSlopeBias)
{
	memcpy( m_pShadowCascadeConstBias, pCascadeConstBias, sizeof(float) * MAX_SHADOW_CASCADES_NUM);
	memcpy( m_pShadowCascadeSlopeBias, pCascadeSlopeBias, sizeof(float) * MAX_SHADOW_CASCADES_NUM);
}

//////////////////////////////////////////////////////////////////////////
bool C3DEngine::CheckIntersectClouds(const Vec3 & p1, const Vec3 & p2)
{
  return m_pCloudsManager->CheckIntersectClouds(p1, p2);
}

void C3DEngine::OnRenderMeshDeleted(IRenderMesh * pRenderMesh)
{
  if(m_pDecalManager)
    m_pDecalManager->OnRenderMeshDeleted(pRenderMesh);
}

bool C3DEngine::RayObjectsIntersection2D( Vec3 vStart, Vec3 vEnd, Vec3 & vHitPoint, EERType eERType )
{
#ifdef SUPPORT_TERRAIN_AO_PRE_COMPUTATIONS

  float fClosestHitDistance = 1000000;

  AABB aabb; aabb.Reset(); aabb.Add(vStart); aabb.Add(vEnd);
  for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); nSID++)
  {
    if(IsSegmentSafeToUse(nSID) && m_pObjectsTree[nSID])
    {
      if(Overlap::AABB_AABB2D(aabb, m_pObjectsTree[nSID]->GetNodeBox())) //!!!mpeykov: 2D?
          m_pObjectsTree[nSID]->RayObjectsIntersection2D( vStart, vEnd, vHitPoint, fClosestHitDistance, eERType );
    }
  }

  return (fClosestHitDistance < 1000000);

#else

  assert(!"C3DEngine::RayObjectsIntersection2D not supported on consoles");
  return 0;

#endif
}

bool C3DEngine::RenderMeshRayIntersection( IRenderMesh *pRenderMesh, SRayHitInfo &hitInfo,IMaterial *pCustomMtl )
{
  return CRenderMeshUtils::RayIntersection(pRenderMesh,hitInfo,pCustomMtl);
}

#include "CryThread.h"
namespace
{
  CryCriticalSection g_cCheckCreateRNTmpData;
}

void C3DEngine::CreateRNTmpData(CRNTmpData ** ppInfo, IRenderNode * pRNode)
{
  assert(!(*ppInfo));

  AUTO_LOCK(g_cCheckCreateRNTmpData);
  if ( gEnv->mMainThreadId != CryGetCurrentThreadId() )
  {
    CryFatalError( "CRNTmpData should only be allocated and free'd on main thread." );
  }

  FUNCTION_PROFILER_3DENGINE;

  // make sure element is allocated
  if(m_LTPRootFree.pNext == &m_LTPRootFree)
  {
    CRNTmpData * pNew = new CRNTmpData;//m_RNTmpDataPools.GetNewElement();
    pNew->Link(&m_LTPRootFree);
  }

  // move element from m_LTPRootFree to m_LTPRootUsed
  CRNTmpData * pElem = m_LTPRootFree.pNext;
  pElem->Unlink();
  pElem->Link(&m_LTPRootUsed);

  *ppInfo = pElem;
  (*ppInfo)->pOwnerRef = ppInfo;

  assert(!(*((*ppInfo)->pOwnerRef))->userData.m_pFoliage);

  memset(&pElem->userData, 0, sizeof(pElem->userData));

  if(pRNode)
    pRNode->OnRenderNodeBecomeVisible();

  (*ppInfo)->nCreatedFrameId = GetMainFrameID();
}

static SBending sBendRemoved;

void C3DEngine::FreeRNTmpData(CRNTmpData ** ppInfo)
{
  AUTO_LOCK(g_cCheckCreateRNTmpData);
  assert((*ppInfo)->pNext != &m_LTPRootFree);
  assert((*ppInfo)->pPrev != &m_LTPRootFree);
  assert((*ppInfo) != &m_LTPRootUsed);
  if ( gEnv->mMainThreadId != CryGetCurrentThreadId() )
  {
    CryFatalError( "CRNTmpData should only be allocated and free'd on main thread." );
  }

  if ( (*ppInfo) != &m_LTPRootUsed )
  {
    (*ppInfo)->Unlink();
  }

  //  (*ppInfo)->ReleaseUserData(GetRenderer());

  {
#ifdef SUPP_HWOBJ_OCCL
    if((*ppInfo)->userData.m_OcclState.pREOcclusionQuery)
    {
      (*ppInfo)->userData.m_OcclState.pREOcclusionQuery->Release(false);
      (*ppInfo)->userData.m_OcclState.pREOcclusionQuery = NULL;
    }
#endif
    if((*ppInfo)->userData.m_pFoliage)
    {
      (*ppInfo)->userData.m_pFoliage->Release();
      (*ppInfo)->userData.m_pFoliage = NULL;
    }

    if((*ppInfo)->userData.pRenderObject)
    {
      GetRenderer()->EF_ObjRemovePermanent((*ppInfo)->userData.pRenderObject);
      (*ppInfo)->userData.pRenderObject = NULL;
    }
  }

  if ( (*ppInfo) != &m_LTPRootUsed )
  {
    (*ppInfo)->Link(&m_LTPRootFree);	
    *((*ppInfo)->pOwnerRef) = NULL;
  }
}

void C3DEngine::UpdateRNTmpDataPool(bool bFreeAll)
{
  FUNCTION_PROFILER_3DENGINE;

  // move old elements into m_LTPRootFree
  CRNTmpData * pNext = NULL;
  for(CRNTmpData * pElem = m_LTPRootUsed.pNext; pElem!=&m_LTPRootUsed; pElem = pNext)
  {
    PrefetchLine(pElem->pNext, 0);
    PrefetchLine(pElem->pNext, offsetof(CRNTmpData, pNext));

    pNext = pElem->pNext;
    if(bFreeAll || (pElem->nLastUsedFrameId < (GetMainFrameID()-GetCVars()->e_RNTmpDataPoolMaxFrames)))
    {
      FreeRNTmpData(&pElem);
    }
  }

  /*
  for(int p=0; p<m_RNTmpDataPools.m_Pools.Count(); p++)
  {
  CRNTmpData * pArray = m_RNTmpDataPools.m_Pools[p];

  for(int e=0; e<m_RNTmpDataPools.GetMaxElemsInChunk(); e++)
  {
  CRNTmpData * pElem = &pArray[e];

  if(pElem->pOwnerRef && *(pElem->pOwnerRef))
  {
  if(bFreeAll || (pElem->nLastUsedFrameId < (GetMainFrameID()-4)))
  {
  FreeRNTmpData(&pElem);
  }
  }
  }
  }*/
}

void C3DEngine::FreeRNTmpDataPool()
{
  // move all into m_LTPRootFree
  UpdateRNTmpDataPool(true);

  AUTO_LOCK(g_cCheckCreateRNTmpData);
  if ( gEnv->mMainThreadId != CryGetCurrentThreadId() )
  {
    CryFatalError( "CRNTmpData should only be allocated and free'd on main thread." );
  }

  // delete all elements of m_LTPRootFree
  CRNTmpData * pNext = NULL;
  for(CRNTmpData * pElem = m_LTPRootFree.pNext; pElem!=&m_LTPRootFree; pElem = pNext)
  {
    pNext = pElem->pNext;
    pElem->Unlink();
    delete pElem;
  }
}

bool C3DEngine::IsAmbientOcclusionEnabled() 
{ 
  return GetTerrain() ? GetTerrain()->IsAmbientOcclusionEnabled() : false; 
}


void C3DEngine::CopyObjectsByType(EERType objType, const AABB *pBox, PodArray<IRenderNode*>* plstObjects)
{
	GetObjectsByTypeGlobal(*plstObjects,objType,pBox);

  if (GetVisAreaManager())
		GetVisAreaManager()->GetObjectsByType(*plstObjects,objType,pBox);
}

void C3DEngine::CopyObjects(const AABB *pBox, PodArray<IRenderNode*>* plstObjects)
{
  for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); nSID++)
    if (IsSegmentSafeToUse(nSID) && m_pObjectsTree[nSID])
	  	m_pObjectsTree[nSID]->GetObjects(*plstObjects,pBox);

  if (GetVisAreaManager())
		GetVisAreaManager()->GetObjects(*plstObjects,pBox);
}

uint32 C3DEngine::GetObjectsByType( EERType objType, IRenderNode **pObjects )
{
	PodArray<IRenderNode*> lstObjects;
	CopyObjectsByType(objType, NULL, &lstObjects);
  if(pObjects && !lstObjects.IsEmpty())
    memcpy(pObjects,&lstObjects[0],lstObjects.GetDataSize());
  return lstObjects.Count();
}

uint32 C3DEngine::GetObjectsByTypeInBox( EERType objType, const AABB &bbox, IRenderNode **pObjects )
{
	PodArray<IRenderNode*> lstObjects;
	CopyObjectsByType(objType, &bbox, &lstObjects);
	if(pObjects && !lstObjects.IsEmpty())
		memcpy(pObjects,&lstObjects[0],lstObjects.GetDataSize());
	return lstObjects.Count();
}

uint32 C3DEngine::GetObjectsInBox(const AABB &bbox, IRenderNode **pObjects)
{
	PodArray<IRenderNode*> lstObjects;
	CopyObjects(&bbox, &lstObjects);
	if(pObjects && !lstObjects.IsEmpty())
		memcpy(pObjects,&lstObjects[0],lstObjects.GetDataSize());
	return lstObjects.Count();
}

int SImageInfo::GetMemoryUsage()
{
  int nSize=0;
  if(detailInfo.pImgMips[0])
    nSize += (int)((float)(detailInfo.nDim*detailInfo.nDim*sizeof(ColorB))*1.3f);
  if(baseInfo.pImgMips[0])
    nSize += (int)((float)(baseInfo.nDim*baseInfo.nDim*sizeof(ColorB))*1.3f);
  return nSize;
}

/*void SImageInfo::InitDetailData(int nLayerId)
{
{
for(int nMip=0; nMip<SImageSubInfo::nMipsNum; nMip++)
SAFE_DELETE_ARRAY(detailInfo.pImgMips[nMip]);
}

if(pSurfType && pSurfType->pLayerMat)
{
SShaderItem & sItem = pSurfType->pLayerMat->GetShaderItem(0);
if(SEfResTexture * pTex = sItem.m_pShaderResources->GetTexture(EFTT_DIFFUSE))
{
if(ITexture * pITex = pTex->m_Sampler.m_pITex)
{
PrintMessage("SImageInfo::InitMaterialData: nLayerId=%d, Dim=%dx%d, pMat=%s ...", 
nLayerId, pITex->GetWidth(), pITex->GetWidth(), pSurfType->pLayerMat ? pSurfType->pLayerMat->GetName() : "NULL");

int nImgSize = pITex->GetWidth()*pITex->GetWidth()*4;
byte * pImage = new byte[nImgSize];   

memcpy(pImage, pITex->GetData32(0,0,0,eTF_A8R8G8B8), nImgSize);

detailInfo.nDim = pITex->GetWidth();

detailInfo.fTiling = 1;
if(pTex->m_bUTile)
detailInfo.fTiling *= pTex->m_TexModificator.m_Tiling[0];
detailInfo.fTiling *= detailInfo.fTilingIn;

byte ** pMips = Get3DEngine()->AllocateMips(pImage, detailInfo.nDim, detailInfo.pImgMips);

PrintMessagePlus(" ok");
}
}
}
}*/

byte ** C3DEngine::AllocateMips(byte*pImage, int nDim, byte ** pImageMips)
{
  memset(pImageMips,0,SImageSubInfo::nMipsNum*sizeof(pImageMips[0]));

  pImageMips[0] = new byte[nDim*nDim*sizeof(ColorB)];
  memcpy(pImageMips[0], pImage, nDim*nDim*sizeof(ColorB));

  ColorB * pMipMain = (ColorB *)pImageMips[0];

  for(int nMip=1; (nDim>>nMip) && nMip<SImageSubInfo::nMipsNum; nMip++)
  {
    int nDimMip = nDim>>nMip;

    int nSubSize = 1<<nMip;

    pImageMips[nMip] = new byte[nDimMip*nDimMip*sizeof(ColorB)];

    ColorB * pMipThis = (ColorB *)pImageMips[nMip];

    for(int x=0; x<nDimMip; x++)
    {
      for(int y=0; y<nDimMip; y++)
      {
        ColorF colSumm(0,0,0,0);
        float fCount = 0;
        for(int _x = x*nSubSize - nSubSize/2; _x < x*nSubSize + nSubSize + nSubSize/2; _x ++)
        {
          for(int _y = y*nSubSize - nSubSize/2; _y < y*nSubSize + nSubSize + nSubSize/2; _y ++)
          {
            int nMask = nDim-1;
            int id = (_x&nMask)*nDim + (_y&nMask);
            colSumm.r += 1.f/255.f*pMipMain[id].r;
            colSumm.g += 1.f/255.f*pMipMain[id].g;
            colSumm.b += 1.f/255.f*pMipMain[id].b;
            colSumm.a += 1.f/255.f*pMipMain[id].a;
            fCount ++;
          }
        }

        colSumm /= fCount;

        colSumm.Clamp(0,1);

        pMipThis[x*nDimMip + y] = colSumm;
      }
    }
  }

  return pImageMips;
}

void C3DEngine::SetTerrainLayerBaseTextureData(int nLayerId, byte*, int, const char * pImgFileName, IMaterial * pMat, float fBr, float fTiling, int nDetailSurfTypeId, float fLayerTilingDetail, float fSpecularAmount, float fSortOrder, ColorF layerFilterColor, float fUseRemeshing, bool bShowSelection)
{
  if(!GetCVars()->e_VoxTer)
    return;

  if(nLayerId<0)
    return;

  PrintMessage("SetTerrainLayerBaseTextureData: nLayerId=%d, nDetailSurfTypeId=%d, fBr=%.1f, pMat=%s ...", nLayerId, nDetailSurfTypeId, fBr, pMat ? pMat->GetName() : "NULL");

  int nMaxCount = max(nLayerId+1, m_arrBaseTextureData.Count());
  m_arrBaseTextureData.PreAllocate(nMaxCount, nMaxCount);

  SImageInfo & rImgInfo = m_arrBaseTextureData[nLayerId];

  ZeroStruct(rImgInfo.arrTextureId);

  if(pMat && strcmp(pMat->GetName(),"Default"))
  {
    strncpy(rImgInfo.szDetMatName, pMat->GetName(),sizeof(rImgInfo.szDetMatName));
    rImgInfo.nPhysSurfaceType = pMat->GetSurfaceTypeId();
  }
  else
  {
    memset(rImgInfo.szDetMatName,0,sizeof(rImgInfo.szDetMatName));
    rImgInfo.nPhysSurfaceType = 0;
  }

  if(pImgFileName)
    strncpy(rImgInfo.szBaseTexName, pImgFileName,sizeof(rImgInfo.szBaseTexName));
  else
    memset(rImgInfo.szBaseTexName,0,sizeof(rImgInfo.szBaseTexName));

  rImgInfo.nDetailSurfTypeId = nDetailSurfTypeId;

  if(m_pVoxTerrain)
  {
    m_pVoxTerrain->RequestTextureUpdate(AABB(Vec3(0),Vec3((float)GetTerrainSize())));

    if(rImgInfo.fBr == fBr && bShowSelection)
    {
      m_pVoxTerrain->SelectLayer(nLayerId);
    }
  }

  rImgInfo.fBr = fBr;
  rImgInfo.layerFilterColor = layerFilterColor;
  rImgInfo.nLayerId = nLayerId;
  rImgInfo.fUseRemeshing = fUseRemeshing;

  rImgInfo.baseInfo.nSortOrder = (int)fSortOrder;
  rImgInfo.baseInfo.fSpecularAmount = fSpecularAmount; 
  rImgInfo.baseInfo.fTiling = rImgInfo.baseInfo.fTilingIn = fTiling;

  rImgInfo.detailInfo.nSortOrder = 0;
  rImgInfo.detailInfo.fSpecularAmount = 0; 
  rImgInfo.detailInfo.fTiling = rImgInfo.detailInfo.fTilingIn = fLayerTilingDetail;  

  for(int nMip=0; nMip<SImageSubInfo::nMipsNum; nMip++)
    SAFE_DELETE_ARRAY(rImgInfo.baseInfo.pImgMips[nMip]);
  rImgInfo.baseInfo.fAmount = 0;

  // base
  /*if(GetCVars()->e_VoxTerTexBuildOnCPU)
  {
  if(ITexture * pITex = GetRenderer()->EF_LoadTexture(rImgInfo.szBaseTexName, FT_DONT_STREAM))
  {
  PrintMessage("SImageInfo::LoadBaseTexture: nLayerId=%d, Dim=%dx%d, pMat=%s ...", 
  nLayerId, pITex->GetWidth(), pITex->GetWidth(), rImgInfo.szBaseTexName);
  int nImgSize = pITex->GetWidth()*pITex->GetWidth()*4;
  byte * pImage = new byte[nImgSize];   
  memcpy(pImage, pITex->GetData32(0,0,0,eTF_A8R8G8B8), nImgSize);
  rImgInfo.baseInfo.nDim = pITex->GetWidth();
  rImgInfo.baseInfo.fTiling = 1;
  rImgInfo.baseInfo.fTiling *= rImgInfo.baseInfo.fTilingIn;
  rImgInfo.baseInfo.fAmount = 1;
  byte ** pMips = Get3DEngine()->AllocateMips(pImage, rImgInfo.baseInfo.nDim, rImgInfo.baseInfo.pImgMips);
  delete [] pImage;
  PrintMessagePlus(" ok");
  }
  }*/

  for(int nMip=0; nMip<SImageSubInfo::nMipsNum; nMip++)
    SAFE_DELETE_ARRAY(rImgInfo.detailInfo.pImgMips[nMip]);
  rImgInfo.detailInfo.fAmount = 0;
  rImgInfo.detailInfo.nDim = 0;

  // detail
  /*  if(GetCVars()->e_VoxTerTexBuildOnCPU && rImgInfo.szDetMatName[0])
  {
  rImgInfo.detailInfo.pMat = pMat;
  rImgInfo.detailInfo.fTiling = rImgInfo.detailInfo.fTilingIn;
  rImgInfo.detailInfo.fAmount = 1;
  }*/

  PrintMessagePlus(" ok");
}

SImageInfo * C3DEngine::GetBaseTextureData(int nLayerId)
{
  if( nLayerId<0 || nLayerId>=m_arrBaseTextureData.Count() )//|| !m_arrBaseTextureData[nLayerId].baseInfo.nDim)
  {
    return NULL;
  }

  return &m_arrBaseTextureData[nLayerId];
}

SImageInfo * C3DEngine::GetBaseTextureDataFromSurfType(int nLayerId)
{
  assert(nLayerId>=0 && nLayerId<MAX_SURFACE_TYPES_COUNT);

  if(nLayerId<0 || nLayerId>=m_arrBaseTextureData.Count() || !m_arrBaseTextureData[nLayerId].baseInfo.nDim)
    return NULL;

  return &m_arrBaseTextureData[nLayerId];
}

void C3DEngine::RegisterForStreaming(IStreamable*pObj)
{
  if(GetObjManager())
    GetObjManager()->RegisterForStreaming(pObj);
}

void C3DEngine::UnregisterForStreaming(IStreamable*pObj)
{
  if(GetObjManager())
    GetObjManager()->UnregisterForStreaming(pObj);
}

void C3DEngine::RenderRenderNode(IShadowCaster * pRNode, SRenderObjectModifier * pROModifier)
{
  switch(pRNode->GetRenderNodeType())
  {
  case eERType_Vegetation:
    {
      CVegetation * pObj = (CVegetation*)pRNode;
      if(pObj->m_dwRndFlags&ERF_HIDDEN)
        return;
      Get3DEngine()->CheckCreateRNTmpData(&pObj->m_pRNTmpData, pObj);
      assert(pObj->m_pRNTmpData->userData.pRenderObject);
      if(!pObj->m_pRNTmpData->userData.pRenderObject)
        return;
      int nLod = pObj->m_pRNTmpData->userData.nLod;
			if(IsRenderingIntoShadowMap() && (pObj->GetDrawFrame(0)<(Cry3DEngineBase::GetFrameID()-10)))
        pObj->m_pRNTmpData->userData.nLod += GetCVars()->e_ShadowsLodBiasInvis;
      pObj->m_pRNTmpData->userData.nLod += GetCVars()->e_ShadowsLodBiasFixed;
      pObj->Render(pROModifier, -1);
      pObj->m_pRNTmpData->userData.nLod = nLod;
    }
    break;

  case eERType_Brush:
    {
      CBrush * pObj = (CBrush*)pRNode;
      if(pObj->m_dwRndFlags&ERF_HIDDEN)
        return;
      Get3DEngine()->CheckCreateRNTmpData(&pObj->m_pRNTmpData, pObj);
      assert(pObj->m_pRNTmpData->userData.pRenderObject);
      if(!pObj->m_pRNTmpData->userData.pRenderObject)
        return;
      int nLod = pObj->m_pRNTmpData->userData.nLod;
      if(IsRenderingIntoShadowMap() && (pObj->GetDrawFrame(0)<(Cry3DEngineBase::GetFrameID()-10)))
        pObj->m_pRNTmpData->userData.nLod += GetCVars()->e_ShadowsLodBiasInvis;
      //disable feature as too many self shadowing issues occured art cannot fix in time
      //pObj->m_pRNTmpData->userData.nLod += GetCVars()->e_ShadowsLodBiasFixed;
      pObj->Render(pROModifier, -1);
      pObj->m_pRNTmpData->userData.nLod = nLod;
    }
    break;

  default:
    {
      SRendParams rParams;

      if(pRNode->IsRenderNode())
      {
        rParams.pRenderNode = (IRenderNode*)pRNode;
        if(rParams.pRenderNode->m_dwRndFlags&ERF_HIDDEN)
          return;
        Get3DEngine()->CheckCreateRNTmpData(&rParams.pRenderNode->m_pRNTmpData, rParams.pRenderNode);
        rParams.nLod = rParams.pRenderNode->m_pRNTmpData ? rParams.pRenderNode->m_pRNTmpData->userData.nLod : 0;
        if(IsRenderingIntoShadowMap() && (rParams.pRenderNode->GetDrawFrame(0)<(Cry3DEngineBase::GetFrameID()-10)))
          rParams.nLod += GetCVars()->e_ShadowsLodBiasInvis;
        rParams.nLod += GetCVars()->e_ShadowsLodBiasFixed;
      }

      if(pROModifier && pROModifier->nStatesInUse)
      {
        rParams.fDistance = pROModifier->fDistance;
      }
      else
      {
        const Vec3 vCamPos = GetCamera().GetPosition();
        const AABB objBox = pRNode->GetBBoxVirtual();
        rParams.fDistance = cry_sqrtf(Distance::Point_AABBSq(vCamPos,objBox))*m_fZoomFactor;
      }

      if(pROModifier && pROModifier->nMatricesInUse)
      {
        rParams.pMatrix = &pROModifier->mat; 
        rParams.pPrevMatrix = &pROModifier->prev_mat; 
      }

      pRNode->Render(rParams);
    }
    break;
  }
}

SImageSubInfo * C3DEngine::GetImageInfo(const char * pName)
{
  if(m_imageInfos.find(string(pName)) != m_imageInfos.end())
    return m_imageInfos[string(pName)];

  return NULL;
}

SImageSubInfo * C3DEngine::RegisterImageInfo(byte ** pMips, int nDim, const char * pName)
{
  if(m_imageInfos.find(string(pName)) != m_imageInfos.end())
    return m_imageInfos[string(pName)];

  assert(pMips && pMips[0]);

  SImageSubInfo * pImgSubInfo = new SImageSubInfo;

  pImgSubInfo->nDim = nDim;

  int nMipDim = pImgSubInfo->nDim;

  for(int m=0; m<SImageSubInfo::nMipsNum && nMipDim; m++)
  {
    pImgSubInfo->pImgMips[m] = new byte[nMipDim*nMipDim*4];

    memcpy(pImgSubInfo->pImgMips[m], pMips[m], nMipDim*nMipDim*4);

    nMipDim /= 2;
  }

  const string strFileName = pName;

  pImgSubInfo->nReady = 1;

  m_imageInfos[strFileName] = pImgSubInfo;

  return pImgSubInfo;
}

/*void C3DEngine::CheckUpdateImageInfos()
{
//  m_imageInfos.clear();

// collect textures from decal render nodes
if(Get3DEngine()->m_pObjectsTree[nSID])
{
static PodArray<IRenderNode*> lstObjects; lstObjects.Clear();

Get3DEngine()->m_pObjectsTree[nSID]->GetObjectsByType(lstObjects, eERType_Decal, NULL);
Get3DEngine()->m_pObjectsTree[nSID]->GetObjectsByType(lstObjects, eERType_Brush, NULL);

for(int d=0; d<lstObjects.Count(); d++)
{
IRenderNode * pNode = lstObjects[d];

if(IMaterial * pMaterial = pNode->GetMaterial())
{
if(pMaterial->GetSubMtlCount())
pMaterial = pMaterial->GetSubMtl(0);

SShaderItem & sItem = pMaterial->GetShaderItem(0);

if(SEfResTexture * pTex = sItem.m_pShaderResources->GetTexture(EFTT_DIFFUSE))
{
if(ITexture * pITex = pTex->m_Sampler.m_pITex)
{
if(byte ** pImgMips = pITex->GetSystemCopy())
{
if(pImgMips[0] && pITex->GetWidth() == pITex->GetHeight())
{
RegisterImageInfo(pImgMips, pITex->GetWidth(), pITex->GetName());
}
}
}
}
}
}
}
}*/


const char * gVoxelEditOperationNames[eveoLast] =
{
  "None",
  "Soft Create",
  "Soft Substract",
  "Hard Create",
  "Hard Substract",
  "Material",
  "Soft Base Color",
  "Blur Positive",
  "Blur Negative",
  "Copy Terrain Pos",
  "Copy Terrain Neg",
  "Pick Height",
  "Integrate Pos",
  "Integrate Neg",
  "Force LOD",
  "Limit LOD",
};

const char * C3DEngine::GetVoxelEditOperationName(EVoxelEditOperation eOperation)
{
  return gVoxelEditOperationNames[eOperation];
}

void C3DEngine::SyncProcessStreamingUpdate() 
{ 
  if( m_pObjManager )
    m_pObjManager->ProcessObjectsStreaming_Finish();
}

void C3DEngine::SetScreenshotCallback(IScreenshotCallback* pCallback)
{
  m_pScreenshotCallback = pCallback;
}

void C3DEngine::ActivateObjectsLayer(uint16 nLayerId, bool bActivate, bool bPhys, const char * pLayerName)
{
  if(!IsAreaActivationInUse())
    return;

  if(bActivate)
    m_bLayersActivated = true;

  if(bActivate && m_nFramesSinceLevelStart<=1)
    m_vPrevMainFrameCamPos.Set(-1000000.f,-1000000.f,-1000000.f);

  FUNCTION_PROFILER_3DENGINE;

  PrintMessage("%s object layer %s (Id = %d) (LevelFrameId = %d)", bActivate ? "Activating" : "Deactivating", pLayerName, nLayerId, m_nFramesSinceLevelStart);

  for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); nSID++)
    if(IsSegmentSafeToUse(nSID) && m_pObjectsTree[nSID])
      m_pObjectsTree[nSID]->ActivateObjectsLayer(nLayerId, bActivate, bPhys);

  if(m_pVisAreaManager)
    m_pVisAreaManager->ActivateObjectsLayer(nLayerId, bActivate, bPhys);
}


void C3DEngine::GetLayerMemoryUsage(uint16 nLayerId, ICrySizer* pSizer, int* pNumBrushes, int* pNumDecals) const
{
	if (pNumBrushes)
		*pNumBrushes = 0;
	if (pNumDecals)
		*pNumDecals = 0;

	for(int nSID=0; nSID<Get3DEngine()->m_pObjectsTree.Count(); ++nSID)
		if(m_pObjectsTree[nSID])
			m_pObjectsTree[nSID]->GetLayerMemoryUsage(nLayerId, pSizer, pNumBrushes, pNumDecals);
}


void C3DEngine::PrecacheCharacter(IRenderNode * pObj, const float fImportance,  ICharacterInstance * pCharacter, IMaterial* pSlotMat, const Matrix34& matParent, const float fEntDistance, const float fScale, int nMaxDepth, bool bForceStreamingSystemUpdate )
{
  if(m_pObjManager)
  {
    m_pObjManager->PrecacheCharacter(pObj, fImportance, pCharacter, pSlotMat, matParent, fEntDistance, fScale, nMaxDepth, false, true, 0 );
    if(bForceStreamingSystemUpdate)
    {
      m_nRenderFrameID = GetRenderer()->GetFrameID();
      m_nRenderMainFrameID = GetRenderer()->GetFrameID(false);
      m_pObjManager->ProcessObjectsStreaming();
    }
  }
}

void C3DEngine::PrecacheRenderNode(IRenderNode * pObj, float fEntDistanceReal)
{
  FUNCTION_PROFILER_3DENGINE;

  //	PrintMessage("==== PrecacheRenderNodePrecacheRenderNode: %s ====", pObj->GetName());

  if(m_pObjManager)
  {
    int dwOldRndFlags = pObj->m_dwRndFlags;
    pObj->m_dwRndFlags &= ~ERF_HIDDEN;
    m_pObjManager->UpdateRenderNodeStreamingPrioriry(pObj, fEntDistanceReal, fEntDistanceReal < GetFloatCVar(e_StreamCgfFastUpdateMaxDistance));
    pObj->m_dwRndFlags = dwOldRndFlags;
  }
}


void C3DEngine::CleanUpOldDecals()
{
  FUNCTION_PROFILER_3DENGINE;
  static uint32 nLastIndex = 0;
  const uint32 nDECALS_PER_FRAME = 50;

  if( uint32 nNumDecalRenderNodes = m_decalRenderNodes.size() )
  {
    for(uint32 i = 0, end = __min(nDECALS_PER_FRAME, nNumDecalRenderNodes); i < end; ++i, ++nLastIndex)
    {
      // wrap around at the end to restart at the beginning
      if( nLastIndex >= nNumDecalRenderNodes )
      {
        nLastIndex = 0;
      }

      m_decalRenderNodes[nLastIndex]->CleanUpOldDecals();
    }
  }
}

void C3DEngine::UpdateRenderTypeEnableLookup()
{
  SetRenderNodeTypeEnabled(eERType_RenderProxy, (GetCVars()->e_Entities != 0));
  SetRenderNodeTypeEnabled(eERType_Brush, (GetCVars()->e_Brushes != 0));
  SetRenderNodeTypeEnabled(eERType_Vegetation, (GetCVars()->e_Vegetation != 0));
}

void C3DEngine::SetRenderNodeMaterialAtPosition( EERType eNodeType, const Vec3 & vPos, IMaterial * pMat )
{
  PodArray<IRenderNode*> lstObjects;

  AABB aabbPos(vPos-Vec3(.1f,.1f,.1f), vPos+Vec3(.1f,.1f,.1f));

  GetObjectsByTypeGlobal(lstObjects,eNodeType,&aabbPos);

  if (GetVisAreaManager())
    GetVisAreaManager()->GetObjectsByType(lstObjects,eNodeType,&aabbPos);

  for(int i=0; i<lstObjects.Count(); i++)
  {
    PrintMessage("Game changed render node material: %s EERType:%d pos: (%d,%d,%d)",
      pMat ? pMat->GetName() : "NULL", 
      (int)eNodeType, 
      (int)vPos.x, (int)vPos.y, (int)vPos.z);

    lstObjects[i]->SetMaterial(pMat);
  }
}

void C3DEngine::SetPrecachePoint( const Vec3 vPos, float fTimeOut )
{
  if(m_pObjManager)
  {
    m_pObjManager->m_vStreamPreCachePoint = vPos;
    m_pObjManager->m_bPreCaheAheadRequested = true;
  }
}

void C3DEngine::DummyFunctionTwo(void)
{
  snprintf(gEnv->szDebugStatus, SSystemGlobalEnvironment::MAX_DEBUG_STRING_LENGTH, "dummyfunctiontwo");
}


void static DrawMeter( float scale, float &x, float &y, int nWidth, int nHeight, IRenderAuxGeom* pAuxRenderer )
{			
	uint16 indLines[ 8 ] = 
	{
		0, 1, 1, 2,
		2, 3, 3, 0
	};

	const int c_yStepSizeTextMeter( 8 );

	const float barWidth = 0.20f; //normalised screen size
	const float yellowStart = 0.5f * barWidth;
	const float redStart = 0.75f * barWidth;

	Vec3 frame[ 4 ] =
	{
		Vec3( ( x - 1 ) / (float) nWidth, ( y - 1 ) / (float) nHeight, 0 ),
		Vec3( x / (float) nWidth + barWidth, ( y - 1 ) / (float) nHeight, 0 ),
		Vec3( x / (float) nWidth + barWidth, ( y + c_yStepSizeTextMeter + 1) / (float) nHeight, 0 ),
		Vec3( ( x - 1 ) / (float) nWidth, ( y + c_yStepSizeTextMeter + 1) / (float) nHeight, 0 )
	};

	pAuxRenderer->DrawLines( frame, 4, indLines, 8, ColorB( 255, 255, 255, 255 ) );

	// draw meter itself
	uint16 indTri[ 6 ] = 
	{
		0, 1, 2, 
		0, 2, 3	
	};	

	// green part (0.0 <= scale <= 0.5)
	{
		float lScale( max( min( scale, 0.5f ), 0.0f ) );

		Vec3 bar[ 4 ] =
		{
			Vec3( x / (float) nWidth, y / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + lScale * barWidth, y / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + lScale * barWidth, ( y + c_yStepSizeTextMeter ) / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth, ( y + c_yStepSizeTextMeter ) / (float) nHeight, 0 )
		};
		pAuxRenderer->DrawTriangles( bar, 4, indTri, 6, ColorB( 0, 255, 0, 255 ) );
	}

	// green to yellow part (0.5 < scale <= 0.75)
	if( scale > 0.5f )
	{
		float lScale( min( scale, 0.75f ) );

		Vec3 bar[ 4 ] =
		{
			Vec3( x / (float) nWidth + yellowStart, y / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + lScale * barWidth, y / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + lScale * barWidth, ( y + c_yStepSizeTextMeter ) / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + yellowStart, ( y + c_yStepSizeTextMeter ) / (float) nHeight, 0 )
		};

		float color[ 4 ];
		// get right color
		if( lScale <= 0.5f )
		{
			color[ 0 ] = 0;
			color[ 1 ] = 1;
			color[ 2 ] = 0;
			color[ 3 ] = 1;
		}
		else if( lScale <= 0.75f )
		{
			color[ 0 ] = ( lScale - 0.5f ) * 4.0f;
			color[ 1 ] = 1;
			color[ 2 ] = 0;
			color[ 3 ] = 1;
		}
		else if( lScale <= 1.0f )
		{
			color[ 0 ] = 1;
			color[ 1 ] = 1 - ( lScale - 0.75f ) * 4.0f;
			color[ 2 ] = 0;
			color[ 3 ] = 1;
		}
		else
		{
			float time( gEnv->pTimer->GetAsyncCurTime() );
			float blink( sinf( time * 6.28f ) * 0.5f + 0.5f );
			color[ 0 ] = 1;
			color[ 1 ] = blink;
			color[ 2 ] = blink;
			color[ 3 ] = 1;
		}

		ColorB colSegStart( 0, 255, 0, 255 );
		ColorB colSegEnd( (uint8) ( color[ 0 ] * 255 ), (uint8) ( color[ 1 ] * 255 ), (uint8) ( color[ 2 ] * 255 ), (uint8) ( color[ 3 ] * 255 ) );

		ColorB col[ 4 ] =
		{
			colSegStart,
			colSegEnd,
			colSegEnd,
			colSegStart
		};

		pAuxRenderer->DrawTriangles( bar, 4, indTri, 6, col );
	}

	// yellow to red part (0.75 < scale <= 1.0)
	if( scale > 0.75f  )
	{
		float lScale( min( scale, 1.0f ) );

		Vec3 bar[ 4 ] =
		{
			Vec3( x / (float) nWidth + redStart, y / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + lScale * barWidth, y / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + lScale * barWidth, ( y + c_yStepSizeTextMeter ) / (float) nHeight, 0 ),
			Vec3( x / (float) nWidth + redStart, ( y + c_yStepSizeTextMeter ) / (float) nHeight, 0 )
		};

		float color[ 4 ];
		if( lScale <= 0.5f )
		{
			color[ 0 ] = 0;
			color[ 1 ] = 1;
			color[ 2 ] = 0;
			color[ 3 ] = 1;
		}
		else if( lScale <= 0.75f )
		{
			color[ 0 ] = ( lScale - 0.5f ) * 4.0f;
			color[ 1 ] = 1;
			color[ 2 ] = 0;
			color[ 3 ] = 1;
		}
		else if( lScale <= 1.0f )
		{
			color[ 0 ] = 1;
			color[ 1 ] = 1 - ( lScale - 0.75f ) * 4.0f;
			color[ 2 ] = 0;
			color[ 3 ] = 1;
		}
		else
		{
			float time( gEnv->pTimer->GetAsyncCurTime() );
			float blink( sinf( time * 6.28f ) * 0.5f + 0.5f );
			color[ 0 ] = 1;
			color[ 1 ] = blink;
			color[ 2 ] = blink;
			color[ 3 ] = 1;
		}

		ColorB colSegStart( 255, 255, 0, 255 );
		ColorB colSegEnd( (uint8) ( color[ 0 ] * 255 ), (uint8) ( color[ 1 ] * 255 ), (uint8) ( color[ 2 ] * 255 ), (uint8) ( color[ 3 ] * 255 ) );

		ColorB col[ 4 ] =
		{
			colSegStart,
			colSegEnd,
			colSegEnd,
			colSegStart
		};

		pAuxRenderer->DrawTriangles( bar, 4, indTri, 6, col );
		
	}
}


void C3DEngine::DrawVegetationProfiler()
{
#if defined(ENABLE_VEGETATION_PROFILER)
	const float fVegetationGPUBudget = 9.0f;
	const float fVegetationCPUBudget = 5.0f;

	float fRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
	float fWhite[] = {1.0f, 1.0f, 1.0f, 1.0f };	
	float fTimeVegetation = 0.0f;
	float fTimeVegetationSprites = 0.0f;
	int nVegetationDP = 0;
	int nVegetationSpritesDP = 0;
	gEnv->pRenderer->GetVegetationProfileTime(fTimeVegetation, fTimeVegetationSprites, nVegetationDP, nVegetationSpritesDP);

	if(!GetCVars()->e_VegetationProfiler)
		return;

	LARGE_INTEGER freq; double frequency; 
	QueryPerformanceFrequency(&freq); frequency = 1.f/static_cast<double>(freq.QuadPart);

	// render bar
	// get height and width of view port
	int nWidth = gEnv->pRenderer->GetWidth();
	int nHeight = gEnv->pRenderer->GetHeight();

	// set to 2D mode for font rendering
	gEnv->pRenderer->Set2DMode( true, nWidth, nHeight );
	IRenderAuxGeom* pAuxRenderer = gEnv->pRenderer->GetIRenderAuxGeom();
	
	//Aux Render setup
	SAuxGeomRenderFlags oldFlags = pAuxRenderer->GetRenderFlags();

	SAuxGeomRenderFlags flags( e_Def2DPublicRenderflags );
	flags.SetDepthTestFlag( e_DepthTestOff );
	flags.SetDepthWriteFlag( e_DepthWriteOff );
	flags.SetCullMode( e_CullModeNone );
	pAuxRenderer->SetRenderFlags( flags );
	
	int nNumVegetationObjects = m_nVegetationObjectsRendered;
	float fTimeMainThread = (float)m_nVegetationFrameTicks/((float)freq.QuadPart / 1000.0f); 

	float x = 10.0f;
	float y = 10.0f;
	
	bool bGPUInRed = fTimeVegetation + fTimeVegetationSprites > fVegetationGPUBudget;
	bool bCPUInRed = fTimeMainThread > fVegetationCPUBudget;

	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, fWhite, false, "== Vegetation Profiler ==" );	y += 15.0f;	
	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, fWhite, false, "Vegetation Objects: %d", nNumVegetationObjects );	y += 15.0f;	
	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, fWhite, false, "Vegetation Drawcalls: %d", nVegetationDP );	y += 15.0f;	
	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, fWhite, false, "Vegetation Sprite Drawcalls: %d", nVegetationSpritesDP );	y += 15.0f;	

	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, fWhite, false, "Vegetation GPU %05.2f ms", fTimeVegetation );	y += 17.0f;	
	DrawMeter( (fTimeVegetation)/fVegetationGPUBudget, x,y, nWidth, nHeight, pAuxRenderer ); 	y += 12.0f;

	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, fWhite, false, "Vegetation Sprites GPU %05.2f ms", fTimeVegetationSprites ); y += 17.0f;	
	DrawMeter( (fTimeVegetationSprites)/fVegetationGPUBudget, x,y, nWidth, nHeight, pAuxRenderer ); 	y += 12.0f;
	
	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, bGPUInRed ? fRed : fWhite, false, "GPU %05.2f( %4.2f ) ms", fTimeVegetation + fTimeVegetationSprites, fVegetationGPUBudget); y += 17.0f;	
	DrawMeter( (fTimeVegetationSprites + fTimeVegetation)/fVegetationGPUBudget, x,y, nWidth, nHeight, pAuxRenderer ); 	y += 12.0f;

	gEnv->pRenderer->Draw2dLabel( x, y, 1.2f, bCPUInRed ? fRed : fWhite, false, "MainThread %05.2f( %4.2f ) ms", fTimeMainThread, fVegetationCPUBudget); y += 17.0f;	
	DrawMeter( (fTimeMainThread)/fVegetationCPUBudget, x,y, nWidth, nHeight, pAuxRenderer ); 	y += 12.0f;

	pAuxRenderer->SetRenderFlags( oldFlags );
	// set back to 3D mode
	gEnv->pRenderer->Set2DMode( false, 0, 0 );

	// reset cpu vegetation profiler
	ResetFrameTicks();
#endif
}

SBending* C3DEngine::GetBendingEntry( SBending* pSrc )
{
	assert(pSrc); 
	unsigned idx = GetMainFrameID()&3; 
	size_t nBendingPoolCapacity = m_bendingPool[idx].size();
	if (m_bendingPoolEntryCount >= nBendingPoolCapacity)
	{
		m_bendingPool[idx].resize(m_bendingPoolEntryCount+1, *pSrc);
	}
	else 
		m_bendingPool[idx][m_bendingPoolEntryCount] = *pSrc; 
	return &(m_bendingPool[idx][m_bendingPoolEntryCount++]);
}

bool C3DEngine::GetTerrainPointInfo(Vec3 vPos, float *pfHeight, Vec3 *pvNormal, ISurfaceType **ppSurfType)
{
	if (!m_pTerrain || !m_pTerrain->GetTerrainPointInfo(vPos, pfHeight, pvNormal, ppSurfType)) 
	{
		if (pfHeight) *pfHeight = 0;
		if (pvNormal) *pvNormal = Vec3(0,0,1);
		if (ppSurfType) *ppSurfType = 0;
		return false;
	}
	return true;
}

void C3DEngine::SetSegmentsManager(ISegmentsManager *pSegmentsManager)
{
	m_pSegmentsManager = pSegmentsManager;
}

bool C3DEngine::IsSegmentedWorldActive()
{
	return m_bSegmentedWorldActive;
}

void C3DEngine::SetSegmentedWorldActive(bool bActive)
{
	m_bSegmentedWorldActive = bActive;
}

#include UNIQUE_VIRTUAL_WRAPPER(IFoliage)
#include UNIQUE_VIRTUAL_WRAPPER(I3DEngine)
DEVIRTUALIZATION_VTABLE_FIX_IMPL(I3DEngine);

